Hotfix Release 2017-02-19c "Frusterick Manners"
[dokuwiki.git] / inc / infoutils.php
bloba124e2c952b9f9bb0179a077ae3b4e40a6f98ede
1 <?php
2 /**
3 * Information and debugging functions
5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author Andreas Gohr <andi@splitbrain.org>
7 */
8 if(!defined('DOKU_INC')) die('meh.');
9 if(!defined('DOKU_MESSAGEURL')) define('DOKU_MESSAGEURL','http://update.dokuwiki.org/check/');
11 /**
12 * Check for new messages from upstream
14 * @author Andreas Gohr <andi@splitbrain.org>
16 function checkUpdateMessages(){
17 global $conf;
18 global $INFO;
19 global $updateVersion;
20 if(!$conf['updatecheck']) return;
21 if($conf['useacl'] && !$INFO['ismanager']) return;
23 $cf = getCacheName($updateVersion, '.updmsg');
24 $lm = @filemtime($cf);
26 // check if new messages needs to be fetched
27 if($lm < time()-(60*60*24) || $lm < @filemtime(DOKU_INC.DOKU_SCRIPT)){
28 @touch($cf);
29 dbglog("checkUpdateMessages(): downloading messages to ".$cf);
30 $http = new DokuHTTPClient();
31 $http->timeout = 12;
32 $resp = $http->get(DOKU_MESSAGEURL.$updateVersion);
33 if(is_string($resp) && ($resp == "" || substr(trim($resp), -1) == '%')) {
34 // basic sanity check that this is either an empty string response (ie "no messages")
35 // or it looks like one of our messages, not WiFi login or other interposed response
36 io_saveFile($cf,$resp);
37 } else {
38 dbglog("checkUpdateMessages(): unexpected HTTP response received");
40 }else{
41 dbglog("checkUpdateMessages(): messages up to date");
44 $data = io_readFile($cf);
45 // show messages through the usual message mechanism
46 $msgs = explode("\n%\n",$data);
47 foreach($msgs as $msg){
48 if($msg) msg($msg,2);
53 /**
54 * Return DokuWiki's version (split up in date and type)
56 * @author Andreas Gohr <andi@splitbrain.org>
58 function getVersionData(){
59 $version = array();
60 //import version string
61 if(file_exists(DOKU_INC.'VERSION')){
62 //official release
63 $version['date'] = trim(io_readFile(DOKU_INC.'VERSION'));
64 $version['type'] = 'Release';
65 }elseif(is_dir(DOKU_INC.'.git')){
66 $version['type'] = 'Git';
67 $version['date'] = 'unknown';
69 $inventory = DOKU_INC.'.git/logs/HEAD';
70 if(is_file($inventory)){
71 $sz = filesize($inventory);
72 $seek = max(0,$sz-2000); // read from back of the file
73 $fh = fopen($inventory,'rb');
74 fseek($fh,$seek);
75 $chunk = fread($fh,2000);
76 fclose($fh);
77 $chunk = trim($chunk);
78 $chunk = @array_pop(explode("\n",$chunk)); //last log line
79 $chunk = @array_shift(explode("\t",$chunk)); //strip commit msg
80 $chunk = explode(" ",$chunk);
81 array_pop($chunk); //strip timezone
82 $date = date('Y-m-d',array_pop($chunk));
83 if($date) $version['date'] = $date;
85 }else{
86 global $updateVersion;
87 $version['date'] = 'update version '.$updateVersion;
88 $version['type'] = 'snapshot?';
90 return $version;
93 /**
94 * Return DokuWiki's version (as a string)
96 * @author Anika Henke <anika@selfthinker.org>
98 function getVersion(){
99 $version = getVersionData();
100 return $version['type'].' '.$version['date'];
104 * Run a few sanity checks
106 * @author Andreas Gohr <andi@splitbrain.org>
108 function check(){
109 global $conf;
110 global $INFO;
111 /* @var Input $INPUT */
112 global $INPUT;
114 if ($INFO['isadmin'] || $INFO['ismanager']){
115 msg('DokuWiki version: '.getVersion(),1);
117 if(version_compare(phpversion(),'5.3.3','<')){
118 msg('Your PHP version is too old ('.phpversion().' vs. 5.3.3+ needed)',-1);
119 }else{
120 msg('PHP version '.phpversion(),1);
122 } else {
123 if(version_compare(phpversion(),'5.3.3','<')){
124 msg('Your PHP version is too old',-1);
128 $mem = (int) php_to_byte(ini_get('memory_limit'));
129 if($mem){
130 if($mem < 16777216){
131 msg('PHP is limited to less than 16MB RAM ('.$mem.' bytes). Increase memory_limit in php.ini',-1);
132 }elseif($mem < 20971520){
133 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);
134 }elseif($mem < 33554432){
135 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);
136 }else{
137 msg('More than 32MB RAM ('.$mem.' bytes) available.',1);
141 if(is_writable($conf['changelog'])){
142 msg('Changelog is writable',1);
143 }else{
144 if (file_exists($conf['changelog'])) {
145 msg('Changelog is not writable',-1);
149 if (isset($conf['changelog_old']) && file_exists($conf['changelog_old'])) {
150 msg('Old changelog exists', 0);
153 if (file_exists($conf['changelog'].'_failed')) {
154 msg('Importing old changelog failed', -1);
155 } else if (file_exists($conf['changelog'].'_importing')) {
156 msg('Importing old changelog now.', 0);
157 } else if (file_exists($conf['changelog'].'_import_ok')) {
158 msg('Old changelog imported', 1);
159 if (!plugin_isdisabled('importoldchangelog')) {
160 msg('Importoldchangelog plugin not disabled after import', -1);
164 if(is_writable(DOKU_CONF)){
165 msg('conf directory is writable',1);
166 }else{
167 msg('conf directory is not writable',-1);
170 if($conf['authtype'] == 'plain'){
171 global $config_cascade;
172 if(is_writable($config_cascade['plainauth.users']['default'])){
173 msg('conf/users.auth.php is writable',1);
174 }else{
175 msg('conf/users.auth.php is not writable',0);
179 if(function_exists('mb_strpos')){
180 if(defined('UTF8_NOMBSTRING')){
181 msg('mb_string extension is available but will not be used',0);
182 }else{
183 msg('mb_string extension is available and will be used',1);
184 if(ini_get('mbstring.func_overload') != 0){
185 msg('mb_string function overloading is enabled, this will cause problems and should be disabled',-1);
188 }else{
189 msg('mb_string extension not available - PHP only replacements will be used',0);
192 if (!UTF8_PREGSUPPORT) {
193 msg('PHP is missing UTF-8 support in Perl-Compatible Regular Expressions (PCRE)', -1);
195 if (!UTF8_PROPERTYSUPPORT) {
196 msg('PHP is missing Unicode properties support in Perl-Compatible Regular Expressions (PCRE)', -1);
199 $loc = setlocale(LC_ALL, 0);
200 if(!$loc){
201 msg('No valid locale is set for your PHP setup. You should fix this',-1);
202 }elseif(stripos($loc,'utf') === false){
203 msg('Your locale <code>'.hsc($loc).'</code> seems not to be a UTF-8 locale, you should fix this if you encounter problems.',0);
204 }else{
205 msg('Valid locale '.hsc($loc).' found.', 1);
208 if($conf['allowdebug']){
209 msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
210 }else{
211 msg('Debugging support is disabled',1);
214 if($INFO['userinfo']['name']){
215 msg('You are currently logged in as '.$INPUT->server->str('REMOTE_USER').' ('.$INFO['userinfo']['name'].')',0);
216 msg('You are part of the groups '.join($INFO['userinfo']['grps'],', '),0);
217 }else{
218 msg('You are currently not logged in',0);
221 msg('Your current permission for this page is '.$INFO['perm'],0);
223 if(is_writable($INFO['filepath'])){
224 msg('The current page is writable by the webserver',0);
225 }else{
226 msg('The current page is not writable by the webserver',0);
229 if($INFO['writable']){
230 msg('The current page is writable by you',0);
231 }else{
232 msg('The current page is not writable by you',0);
235 // Check for corrupted search index
236 $lengths = idx_listIndexLengths();
237 $index_corrupted = false;
238 foreach ($lengths as $length) {
239 if (count(idx_getIndex('w', $length)) != count(idx_getIndex('i', $length))) {
240 $index_corrupted = true;
241 break;
245 foreach (idx_getIndex('metadata', '') as $index) {
246 if (count(idx_getIndex($index.'_w', '')) != count(idx_getIndex($index.'_i', ''))) {
247 $index_corrupted = true;
248 break;
252 if($index_corrupted) {
253 msg(
254 'The search index is corrupted. It might produce wrong results and most
255 probably needs to be rebuilt. See
256 <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
257 for ways to rebuild the search index.', -1
259 } elseif(!empty($lengths)) {
260 msg('The search index seems to be working', 1);
261 } else {
262 msg(
263 'The search index is empty. See
264 <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
265 for help on how to fix the search index. If the default indexer
266 isn\'t used or the wiki is actually empty this is normal.'
270 // rough time check
271 $http = new DokuHTTPClient();
272 $http->max_redirect = 0;
273 $http->timeout = 3;
274 $http->sendRequest('http://www.dokuwiki.org', '', 'HEAD');
275 $now = time();
276 if(isset($http->resp_headers['date'])) {
277 $time = strtotime($http->resp_headers['date']);
278 $diff = $time - $now;
280 if(abs($diff) < 4) {
281 msg("Server time seems to be okay. Diff: {$diff}s", 1);
282 } else {
283 msg("Your server's clock seems to be out of sync! Consider configuring a sync with a NTP server. Diff: {$diff}s");
290 * print a message
292 * If HTTP headers were not sent yet the message is added
293 * to the global message array else it's printed directly
294 * using html_msgarea()
297 * Levels can be:
299 * -1 error
300 * 0 info
301 * 1 success
303 * @author Andreas Gohr <andi@splitbrain.org>
304 * @see html_msgarea
307 define('MSG_PUBLIC', 0);
308 define('MSG_USERS_ONLY', 1);
309 define('MSG_MANAGERS_ONLY',2);
310 define('MSG_ADMINS_ONLY',4);
313 * Display a message to the user
315 * @param string $message
316 * @param int $lvl -1 = error, 0 = info, 1 = success, 2 = notify
317 * @param string $line line number
318 * @param string $file file number
319 * @param int $allow who's allowed to see the message, see MSG_* constants
321 function msg($message,$lvl=0,$line='',$file='',$allow=MSG_PUBLIC){
322 global $MSG, $MSG_shown;
323 $errors = array();
324 $errors[-1] = 'error';
325 $errors[0] = 'info';
326 $errors[1] = 'success';
327 $errors[2] = 'notify';
329 if($line || $file) $message.=' ['.utf8_basename($file).':'.$line.']';
331 if(!isset($MSG)) $MSG = array();
332 $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message, 'allow' => $allow);
333 if(isset($MSG_shown) || headers_sent()){
334 if(function_exists('html_msgarea')){
335 html_msgarea();
336 }else{
337 print "ERROR($lvl) $message";
339 unset($GLOBALS['MSG']);
343 * Determine whether the current user is allowed to view the message
344 * in the $msg data structure
346 * @param $msg array dokuwiki msg structure
347 * msg => string, the message
348 * lvl => int, level of the message (see msg() function)
349 * allow => int, flag used to determine who is allowed to see the message
350 * see MSG_* constants
351 * @return bool
353 function info_msg_allowed($msg){
354 global $INFO, $auth;
356 // is the message public? - everyone and anyone can see it
357 if (empty($msg['allow']) || ($msg['allow'] == MSG_PUBLIC)) return true;
359 // restricted msg, but no authentication
360 if (empty($auth)) return false;
362 switch ($msg['allow']){
363 case MSG_USERS_ONLY:
364 return !empty($INFO['userinfo']);
366 case MSG_MANAGERS_ONLY:
367 return $INFO['ismanager'];
369 case MSG_ADMINS_ONLY:
370 return $INFO['isadmin'];
372 default:
373 trigger_error('invalid msg allow restriction. msg="'.$msg['msg'].'" allow='.$msg['allow'].'"', E_USER_WARNING);
374 return $INFO['isadmin'];
377 return false;
381 * print debug messages
383 * little function to print the content of a var
385 * @author Andreas Gohr <andi@splitbrain.org>
387 function dbg($msg,$hidden=false){
388 if($hidden){
389 echo "<!--\n";
390 print_r($msg);
391 echo "\n-->";
392 }else{
393 echo '<pre class="dbg">';
394 echo hsc(print_r($msg,true));
395 echo '</pre>';
400 * Print info to a log file
402 * @author Andreas Gohr <andi@splitbrain.org>
404 function dbglog($msg,$header=''){
405 global $conf;
406 /* @var Input $INPUT */
407 global $INPUT;
409 // The debug log isn't automatically cleaned thus only write it when
410 // debugging has been enabled by the user.
411 if($conf['allowdebug'] !== 1) return;
412 if(is_object($msg) || is_array($msg)){
413 $msg = print_r($msg,true);
416 if($header) $msg = "$header\n$msg";
418 $file = $conf['cachedir'].'/debug.log';
419 $fh = fopen($file,'a');
420 if($fh){
421 fwrite($fh,date('H:i:s ').$INPUT->server->str('REMOTE_ADDR').': '.$msg."\n");
422 fclose($fh);
427 * Log accesses to deprecated fucntions to the debug log
429 * @param string $alternative The function or method that should be used instead
431 function dbg_deprecated($alternative = '') {
432 global $conf;
433 if(!$conf['allowdebug']) return;
435 $backtrace = debug_backtrace();
436 array_shift($backtrace);
437 $self = array_shift($backtrace);
438 $call = array_shift($backtrace);
440 $called = trim($self['class'].'::'.$self['function'].'()', ':');
441 $caller = trim($call['class'].'::'.$call['function'].'()', ':');
443 $msg = $called.' is deprecated. It was called from ';
444 $msg .= $caller.' in '.$call['file'].':'.$call['line'];
445 if($alternative) {
446 $msg .= ' '.$alternative.' should be used instead!';
449 dbglog($msg);
453 * Print a reversed, prettyprinted backtrace
455 * @author Gary Owen <gary_owen@bigfoot.com>
457 function dbg_backtrace(){
458 // Get backtrace
459 $backtrace = debug_backtrace();
461 // Unset call to debug_print_backtrace
462 array_shift($backtrace);
464 // Iterate backtrace
465 $calls = array();
466 $depth = count($backtrace) - 1;
467 foreach ($backtrace as $i => $call) {
468 $location = $call['file'] . ':' . $call['line'];
469 $function = (isset($call['class'])) ?
470 $call['class'] . $call['type'] . $call['function'] : $call['function'];
472 $params = array();
473 if (isset($call['args'])){
474 foreach($call['args'] as $arg){
475 if(is_object($arg)){
476 $params[] = '[Object '.get_class($arg).']';
477 }elseif(is_array($arg)){
478 $params[] = '[Array]';
479 }elseif(is_null($arg)){
480 $params[] = '[NULL]';
481 }else{
482 $params[] = (string) '"'.$arg.'"';
486 $params = implode(', ',$params);
488 $calls[$depth - $i] = sprintf('%s(%s) called at %s',
489 $function,
490 str_replace("\n", '\n', $params),
491 $location);
493 ksort($calls);
495 return implode("\n", $calls);
499 * Remove all data from an array where the key seems to point to sensitive data
501 * This is used to remove passwords, mail addresses and similar data from the
502 * debug output
504 * @author Andreas Gohr <andi@splitbrain.org>
506 function debug_guard(&$data){
507 foreach($data as $key => $value){
508 if(preg_match('/(notify|pass|auth|secret|ftp|userinfo|token|buid|mail|proxy)/i',$key)){
509 $data[$key] = '***';
510 continue;
512 if(is_array($value)) debug_guard($data[$key]);