Completed
Push — fixGetVersion ( 067b4f...f519f9 )
by Michael
04:26
created

infoutils.php ➔ getVersionData()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 8
nop 0
dl 0
loc 40
rs 8.0355
c 0
b 0
f 0
1
<?php
2
/**
3
 * Information and debugging functions
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 */
8
if(!defined('DOKU_INC')) die('meh.');
9
10
if(!defined('DOKU_MESSAGEURL')){
11
    if(in_array('ssl', stream_get_transports())) {
12
        define('DOKU_MESSAGEURL','https://update.dokuwiki.org/check/');
13
    }else{
14
        define('DOKU_MESSAGEURL','http://update.dokuwiki.org/check/');
15
    }
16
}
17
18
/**
19
 * Check for new messages from upstream
20
 *
21
 * @author Andreas Gohr <[email protected]>
22
 */
23
function checkUpdateMessages(){
24
    global $conf;
25
    global $INFO;
26
    global $updateVersion;
27
    if(!$conf['updatecheck']) return;
28
    if($conf['useacl'] && !$INFO['ismanager']) return;
29
30
    $cf = getCacheName($updateVersion, '.updmsg');
31
    $lm = @filemtime($cf);
32
    $is_http = substr(DOKU_MESSAGEURL, 0, 5) != 'https';
33
34
    // check if new messages needs to be fetched
35
    if($lm < time()-(60*60*24) || $lm < @filemtime(DOKU_INC.DOKU_SCRIPT)){
36
        @touch($cf);
37
        dbglog("checkUpdateMessages(): downloading messages to ".$cf.($is_http?' (without SSL)':' (with SSL)'));
38
        $http = new DokuHTTPClient();
39
        $http->timeout = 12;
40
        $resp = $http->get(DOKU_MESSAGEURL.$updateVersion);
41
        if(is_string($resp) && ($resp == "" || substr(trim($resp), -1) == '%')) {
42
            // basic sanity check that this is either an empty string response (ie "no messages")
43
            // or it looks like one of our messages, not WiFi login or other interposed response
44
            io_saveFile($cf,$resp);
45
        } else {
46
            dbglog("checkUpdateMessages(): unexpected HTTP response received");
47
        }
48
    }else{
49
        dbglog("checkUpdateMessages(): messages up to date");
50
    }
51
52
    $data = io_readFile($cf);
53
    // show messages through the usual message mechanism
54
    $msgs = explode("\n%\n",$data);
55
    foreach($msgs as $msg){
56
        if($msg) msg($msg,2);
57
    }
58
}
59
60
61
/**
62
 * Return DokuWiki's version (split up in date and type)
63
 *
64
 * @author Andreas Gohr <[email protected]>
65
 */
66
function getVersionData(){
67
    $version = array();
68
    //import version string
69
    if(file_exists(DOKU_INC.'VERSION')){
70
        //official release
71
        $version['date'] = trim(io_readFile(DOKU_INC.'VERSION'));
72
        $version['type'] = 'Release';
73
    }elseif(is_dir(DOKU_INC.'.git')){
74
        $version['type'] = 'Git';
75
        $version['date'] = 'unknown';
76
77
        if ($date = shell_exec("git log -1 --pretty=format:'%cd' --date=short")) {
78
            $version['date'] = hsc($date);
79
        } else if (file_exists(DOKU_INC . '.git/HEAD')) {
80
            // we cannot use git on the shell -- let's do it manually!
81
            $headCommit = trim(file_get_contents(DOKU_INC . '.git/HEAD'));
82
            if (strpos($headCommit, 'ref: ') === 0) {
83
                // it is something like `ref: refs/heads/master`
84
                $pathToHead = substr($headCommit, 5);
85
                $headCommit = trim(file_get_contents(DOKU_INC . '.git/' . $pathToHead));
86
            }
87
            $subDir = substr($headCommit, 0, 2);
88
            $fileName = substr($headCommit, 2);
89
            $getCommitObject = DOKU_INC . ".git/objects/$subDir/$fileName";
90
            $commit = zlib_decode(file_get_contents($getCommitObject));
91
            $committerLine = explode("\n", $commit)[3];
92
            $committerData = explode(' ', $committerLine);
93
            end($committerData);
94
            $ts = prev($committerData);
95
            if ($ts && $date = date('Y-m-d', $ts)) {
96
                $version['date'] = $date;
97
            }
98
        }
99
    }else{
100
        global $updateVersion;
101
        $version['date'] = 'update version '.$updateVersion;
102
        $version['type'] = 'snapshot?';
103
    }
104
    return $version;
105
}
106
107
/**
108
 * Return DokuWiki's version (as a string)
109
 *
110
 * @author Anika Henke <[email protected]>
111
 */
112
function getVersion(){
113
    $version = getVersionData();
114
    return $version['type'].' '.$version['date'];
115
}
116
117
/**
118
 * Run a few sanity checks
119
 *
120
 * @author Andreas Gohr <[email protected]>
121
 */
122
function check(){
123
    global $conf;
124
    global $INFO;
125
    /* @var Input $INPUT */
126
    global $INPUT;
127
128
    if ($INFO['isadmin'] || $INFO['ismanager']){
129
        msg('DokuWiki version: '.getVersion(),1);
130
131
        if(version_compare(phpversion(),'5.6.0','<')){
132
            msg('Your PHP version is too old ('.phpversion().' vs. 5.6.0+ needed)',-1);
133
        }else{
134
            msg('PHP version '.phpversion(),1);
135
        }
136
    } else {
137
        if(version_compare(phpversion(),'5.6.0','<')){
138
            msg('Your PHP version is too old',-1);
139
        }
140
    }
141
142
    $mem = (int) php_to_byte(ini_get('memory_limit'));
143
    if($mem){
144
        if($mem < 16777216){
145
            msg('PHP is limited to less than 16MB RAM ('.$mem.' bytes). Increase memory_limit in php.ini',-1);
146
        }elseif($mem < 20971520){
147
            msg('PHP is limited to less than 20MB RAM ('.$mem.' bytes), you might encounter problems with bigger pages. Increase memory_limit in php.ini',-1);
148
        }elseif($mem < 33554432){
149
            msg('PHP is limited to less than 32MB RAM ('.$mem.' bytes), but that should be enough in most cases. If not, increase memory_limit in php.ini',0);
150
        }else{
151
            msg('More than 32MB RAM ('.$mem.' bytes) available.',1);
152
        }
153
    }
154
155
    if(is_writable($conf['changelog'])){
156
        msg('Changelog is writable',1);
157
    }else{
158
        if (file_exists($conf['changelog'])) {
159
            msg('Changelog is not writable',-1);
160
        }
161
    }
162
163
    if (isset($conf['changelog_old']) && file_exists($conf['changelog_old'])) {
164
        msg('Old changelog exists', 0);
165
    }
166
167
    if (file_exists($conf['changelog'].'_failed')) {
168
        msg('Importing old changelog failed', -1);
169
    } else if (file_exists($conf['changelog'].'_importing')) {
170
        msg('Importing old changelog now.', 0);
171
    } else if (file_exists($conf['changelog'].'_import_ok')) {
172
        msg('Old changelog imported', 1);
173
        if (!plugin_isdisabled('importoldchangelog')) {
174
            msg('Importoldchangelog plugin not disabled after import', -1);
175
        }
176
    }
177
178
    if(is_writable(DOKU_CONF)){
179
        msg('conf directory is writable',1);
180
    }else{
181
        msg('conf directory is not writable',-1);
182
    }
183
184
    if($conf['authtype'] == 'plain'){
185
        global $config_cascade;
186
        if(is_writable($config_cascade['plainauth.users']['default'])){
187
            msg('conf/users.auth.php is writable',1);
188
        }else{
189
            msg('conf/users.auth.php is not writable',0);
190
        }
191
    }
192
193
    if(function_exists('mb_strpos')){
194
        if(defined('UTF8_NOMBSTRING')){
195
            msg('mb_string extension is available but will not be used',0);
196
        }else{
197
            msg('mb_string extension is available and will be used',1);
198
            if(ini_get('mbstring.func_overload') != 0){
199
                msg('mb_string function overloading is enabled, this will cause problems and should be disabled',-1);
200
            }
201
        }
202
    }else{
203
        msg('mb_string extension not available - PHP only replacements will be used',0);
204
    }
205
206
    if (!UTF8_PREGSUPPORT) {
207
        msg('PHP is missing UTF-8 support in Perl-Compatible Regular Expressions (PCRE)', -1);
208
    }
209
    if (!UTF8_PROPERTYSUPPORT) {
210
        msg('PHP is missing Unicode properties support in Perl-Compatible Regular Expressions (PCRE)', -1);
211
    }
212
213
    $loc = setlocale(LC_ALL, 0);
214
    if(!$loc){
215
        msg('No valid locale is set for your PHP setup. You should fix this',-1);
216
    }elseif(stripos($loc,'utf') === false){
217
        msg('Your locale <code>'.hsc($loc).'</code> seems not to be a UTF-8 locale, you should fix this if you encounter problems.',0);
218
    }else{
219
        msg('Valid locale '.hsc($loc).' found.', 1);
220
    }
221
222
    if($conf['allowdebug']){
223
        msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
224
    }else{
225
        msg('Debugging support is disabled',1);
226
    }
227
228
    if($INFO['userinfo']['name']){
229
        msg('You are currently logged in as '.$INPUT->server->str('REMOTE_USER').' ('.$INFO['userinfo']['name'].')',0);
230
        msg('You are part of the groups '.join($INFO['userinfo']['grps'],', '),0);
231
    }else{
232
        msg('You are currently not logged in',0);
233
    }
234
235
    msg('Your current permission for this page is '.$INFO['perm'],0);
236
237
    if(is_writable($INFO['filepath'])){
238
        msg('The current page is writable by the webserver',0);
239
    }else{
240
        msg('The current page is not writable by the webserver',0);
241
    }
242
243
    if($INFO['writable']){
244
        msg('The current page is writable by you',0);
245
    }else{
246
        msg('The current page is not writable by you',0);
247
    }
248
249
    // Check for corrupted search index
250
    $lengths = idx_listIndexLengths();
251
    $index_corrupted = false;
252
    foreach ($lengths as $length) {
253
        if (count(idx_getIndex('w', $length)) != count(idx_getIndex('i', $length))) {
254
            $index_corrupted = true;
255
            break;
256
        }
257
    }
258
259
    foreach (idx_getIndex('metadata', '') as $index) {
260
        if (count(idx_getIndex($index.'_w', '')) != count(idx_getIndex($index.'_i', ''))) {
261
            $index_corrupted = true;
262
            break;
263
        }
264
    }
265
266
    if($index_corrupted) {
267
        msg(
268
            'The search index is corrupted. It might produce wrong results and most
269
                probably needs to be rebuilt. See
270
                <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
271
                for ways to rebuild the search index.', -1
272
        );
273
    } elseif(!empty($lengths)) {
274
        msg('The search index seems to be working', 1);
275
    } else {
276
        msg(
277
            'The search index is empty. See
278
                <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
279
                for help on how to fix the search index. If the default indexer
280
                isn\'t used or the wiki is actually empty this is normal.'
281
        );
282
    }
283
284
    // rough time check
285
    $http = new DokuHTTPClient();
286
    $http->max_redirect = 0;
287
    $http->timeout = 3;
288
    $http->sendRequest('http://www.dokuwiki.org', '', 'HEAD');
289
    $now = time();
290
    if(isset($http->resp_headers['date'])) {
291
        $time = strtotime($http->resp_headers['date']);
292
        $diff = $time - $now;
293
294
        if(abs($diff) < 4) {
295
            msg("Server time seems to be okay. Diff: {$diff}s", 1);
296
        } else {
297
            msg("Your server's clock seems to be out of sync! Consider configuring a sync with a NTP server.  Diff: {$diff}s");
298
        }
299
    }
300
301
}
302
303
/**
304
 * print a message
305
 *
306
 * If HTTP headers were not sent yet the message is added
307
 * to the global message array else it's printed directly
308
 * using html_msgarea()
309
 *
310
 *
311
 * Levels can be:
312
 *
313
 * -1 error
314
 *  0 info
315
 *  1 success
316
 *
317
 * @author Andreas Gohr <[email protected]>
318
 * @see    html_msgarea
319
 */
320
321
define('MSG_PUBLIC', 0);
322
define('MSG_USERS_ONLY', 1);
323
define('MSG_MANAGERS_ONLY',2);
324
define('MSG_ADMINS_ONLY',4);
325
326
/**
327
 * Display a message to the user
328
 *
329
 * @param string $message
330
 * @param int    $lvl   -1 = error, 0 = info, 1 = success, 2 = notify
331
 * @param string $line  line number
332
 * @param string $file  file number
333
 * @param int    $allow who's allowed to see the message, see MSG_* constants
334
 */
335
function msg($message,$lvl=0,$line='',$file='',$allow=MSG_PUBLIC){
336
    global $MSG, $MSG_shown;
337
    $errors = array();
338
    $errors[-1] = 'error';
339
    $errors[0]  = 'info';
340
    $errors[1]  = 'success';
341
    $errors[2]  = 'notify';
342
343
    if($line || $file) $message.=' ['.utf8_basename($file).':'.$line.']';
344
345
    if(!isset($MSG)) $MSG = array();
346
    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message, 'allow' => $allow);
347
    if(isset($MSG_shown) || headers_sent()){
348
        if(function_exists('html_msgarea')){
349
            html_msgarea();
350
        }else{
351
            print "ERROR($lvl) $message";
352
        }
353
        unset($GLOBALS['MSG']);
354
    }
355
}
356
/**
357
 * Determine whether the current user is allowed to view the message
358
 * in the $msg data structure
359
 *
360
 * @param  $msg   array    dokuwiki msg structure
361
 *                         msg   => string, the message
362
 *                         lvl   => int, level of the message (see msg() function)
363
 *                         allow => int, flag used to determine who is allowed to see the message
364
 *                                       see MSG_* constants
365
 * @return bool
366
 */
367
function info_msg_allowed($msg){
368
    global $INFO, $auth;
369
370
    // is the message public? - everyone and anyone can see it
371
    if (empty($msg['allow']) || ($msg['allow'] == MSG_PUBLIC)) return true;
372
373
    // restricted msg, but no authentication
374
    if (empty($auth)) return false;
375
376
    switch ($msg['allow']){
377
        case MSG_USERS_ONLY:
378
            return !empty($INFO['userinfo']);
379
380
        case MSG_MANAGERS_ONLY:
381
            return $INFO['ismanager'];
382
383
        case MSG_ADMINS_ONLY:
384
            return $INFO['isadmin'];
385
386
        default:
387
            trigger_error('invalid msg allow restriction.  msg="'.$msg['msg'].'" allow='.$msg['allow'].'"', E_USER_WARNING);
388
            return $INFO['isadmin'];
389
    }
390
391
    return false;
0 ignored issues
show
Unused Code introduced by
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
392
}
393
394
/**
395
 * print debug messages
396
 *
397
 * little function to print the content of a var
398
 *
399
 * @author Andreas Gohr <[email protected]>
400
 *
401
 * @param string $msg
402
 * @param bool $hidden
403
 */
404
function dbg($msg,$hidden=false){
405
    if($hidden){
406
        echo "<!--\n";
407
        print_r($msg);
408
        echo "\n-->";
409
    }else{
410
        echo '<pre class="dbg">';
411
        echo hsc(print_r($msg,true));
412
        echo '</pre>';
413
    }
414
}
415
416
/**
417
 * Print info to a log file
418
 *
419
 * @author Andreas Gohr <[email protected]>
420
 *
421
 * @param string $msg
422
 * @param string $header
423
 */
424
function dbglog($msg,$header=''){
425
    global $conf;
426
    /* @var Input $INPUT */
427
    global $INPUT;
428
429
    // The debug log isn't automatically cleaned thus only write it when
430
    // debugging has been enabled by the user.
431
    if($conf['allowdebug'] !== 1) return;
432
    if(is_object($msg) || is_array($msg)){
433
        $msg = print_r($msg,true);
434
    }
435
436
    if($header) $msg = "$header\n$msg";
437
438
    $file = $conf['cachedir'].'/debug.log';
439
    $fh = fopen($file,'a');
440
    if($fh){
441
        fwrite($fh,date('H:i:s ').$INPUT->server->str('REMOTE_ADDR').': '.$msg."\n");
442
        fclose($fh);
443
    }
444
}
445
446
/**
447
 * Log accesses to deprecated fucntions to the debug log
448
 *
449
 * @param string $alternative The function or method that should be used instead
450
 * @triggers INFO_DEPRECATION_LOG
451
 */
452
function dbg_deprecated($alternative = '') {
453
    global $conf;
454
    global $EVENT_HANDLER;
455
    if(!$conf['allowdebug'] && !$EVENT_HANDLER->hasHandlerForEvent('INFO_DEPRECATION_LOG')) {
456
        // avoid any work if no one cares
457
        return;
458
    }
459
460
    $backtrace = debug_backtrace();
461
    array_shift($backtrace);
462
    $self = $backtrace[0];
463
    $call = $backtrace[1];
464
465
    $data = [
466
        'trace' => $backtrace,
467
        'alternative' => $alternative,
468
        'called' => trim($self['class'] . '::' . $self['function'] . '()', ':'),
469
        'caller' => trim($call['class'] . '::' . $call['function'] . '()', ':'),
470
        'file' => $call['file'],
471
        'line' => $call['line'],
472
    ];
473
474
    $event = new Doku_Event('INFO_DEPRECATION_LOG', $data);
475
    if($event->advise_before()) {
476
        $msg = $event->data['called'] . ' is deprecated. It was called from ';
477
        $msg .= $event->data['caller'] . ' in ' . $event->data['file'] . ':' . $event->data['line'];
478
        if($event->data['alternative']) {
479
            $msg .= ' ' . $event->data['alternative'] . ' should be used instead!';
480
        }
481
        dbglog($msg);
482
    }
483
    $event->advise_after();
484
}
485
486
/**
487
 * Print a reversed, prettyprinted backtrace
488
 *
489
 * @author Gary Owen <[email protected]>
490
 */
491
function dbg_backtrace(){
492
    // Get backtrace
493
    $backtrace = debug_backtrace();
494
495
    // Unset call to debug_print_backtrace
496
    array_shift($backtrace);
497
498
    // Iterate backtrace
499
    $calls = array();
500
    $depth = count($backtrace) - 1;
501
    foreach ($backtrace as $i => $call) {
502
        $location = $call['file'] . ':' . $call['line'];
503
        $function = (isset($call['class'])) ?
504
            $call['class'] . $call['type'] . $call['function'] : $call['function'];
505
506
        $params = array();
507
        if (isset($call['args'])){
508
            foreach($call['args'] as $arg){
509
                if(is_object($arg)){
510
                    $params[] = '[Object '.get_class($arg).']';
511
                }elseif(is_array($arg)){
512
                    $params[] = '[Array]';
513
                }elseif(is_null($arg)){
514
                    $params[] = '[NULL]';
515
                }else{
516
                    $params[] = (string) '"'.$arg.'"';
517
                }
518
            }
519
        }
520
        $params = implode(', ',$params);
521
522
        $calls[$depth - $i] = sprintf('%s(%s) called at %s',
523
                $function,
524
                str_replace("\n", '\n', $params),
525
                $location);
526
    }
527
    ksort($calls);
528
529
    return implode("\n", $calls);
530
}
531
532
/**
533
 * Remove all data from an array where the key seems to point to sensitive data
534
 *
535
 * This is used to remove passwords, mail addresses and similar data from the
536
 * debug output
537
 *
538
 * @author Andreas Gohr <[email protected]>
539
 *
540
 * @param array $data
541
 */
542
function debug_guard(&$data){
543
    foreach($data as $key => $value){
544
        if(preg_match('/(notify|pass|auth|secret|ftp|userinfo|token|buid|mail|proxy)/i',$key)){
545
            $data[$key] = '***';
546
            continue;
547
        }
548
        if(is_array($value)) debug_guard($data[$key]);
549
    }
550
}
551