[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> infoutils.php (source)

   1  <?php
   2  /**
   3   * Information and debugging functions
   4   *
   5   * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
   6   * @author     Andreas Gohr <andi@splitbrain.org>
   7   */
   8  
   9  use dokuwiki\HTTP\DokuHTTPClient;
  10  
  11  if(!defined('DOKU_MESSAGEURL')){
  12      if(in_array('ssl', stream_get_transports())) {
  13          define('DOKU_MESSAGEURL','https://update.dokuwiki.org/check/');
  14      }else{
  15          define('DOKU_MESSAGEURL','http://update.dokuwiki.org/check/');
  16      }
  17  }
  18  
  19  /**
  20   * Check for new messages from upstream
  21   *
  22   * @author Andreas Gohr <andi@splitbrain.org>
  23   */
  24  function checkUpdateMessages(){
  25      global $conf;
  26      global $INFO;
  27      global $updateVersion;
  28      if(!$conf['updatecheck']) return;
  29      if($conf['useacl'] && !$INFO['ismanager']) return;
  30  
  31      $cf = getCacheName($updateVersion, '.updmsg');
  32      $lm = @filemtime($cf);
  33      $is_http = substr(DOKU_MESSAGEURL, 0, 5) != 'https';
  34  
  35      // check if new messages needs to be fetched
  36      if($lm < time()-(60*60*24) || $lm < @filemtime(DOKU_INC.DOKU_SCRIPT)){
  37          @touch($cf);
  38          dbglog("checkUpdateMessages(): downloading messages to ".$cf.($is_http?' (without SSL)':' (with SSL)'));
  39          $http = new DokuHTTPClient();
  40          $http->timeout = 12;
  41          $resp = $http->get(DOKU_MESSAGEURL.$updateVersion);
  42          if(is_string($resp) && ($resp == "" || substr(trim($resp), -1) == '%')) {
  43              // basic sanity check that this is either an empty string response (ie "no messages")
  44              // or it looks like one of our messages, not WiFi login or other interposed response
  45              io_saveFile($cf,$resp);
  46          } else {
  47              dbglog("checkUpdateMessages(): unexpected HTTP response received");
  48          }
  49      }else{
  50          dbglog("checkUpdateMessages(): messages up to date");
  51      }
  52  
  53      $data = io_readFile($cf);
  54      // show messages through the usual message mechanism
  55      $msgs = explode("\n%\n",$data);
  56      foreach($msgs as $msg){
  57          if($msg) msg($msg,2);
  58      }
  59  }
  60  
  61  
  62  /**
  63   * Return DokuWiki's version (split up in date and type)
  64   *
  65   * @author Andreas Gohr <andi@splitbrain.org>
  66   */
  67  function getVersionData(){
  68      $version = array();
  69      //import version string
  70      if(file_exists(DOKU_INC.'VERSION')){
  71          //official release
  72          $version['date'] = trim(io_readFile(DOKU_INC.'VERSION'));
  73          $version['type'] = 'Release';
  74      }elseif(is_dir(DOKU_INC.'.git')){
  75          $version['type'] = 'Git';
  76          $version['date'] = 'unknown';
  77  
  78          if ($date = shell_exec("git log -1 --pretty=format:'%cd' --date=short")) {
  79              $version['date'] = hsc($date);
  80          } else if (file_exists(DOKU_INC . '.git/HEAD')) {
  81              // we cannot use git on the shell -- let's do it manually!
  82              $headCommit = trim(file_get_contents(DOKU_INC . '.git/HEAD'));
  83              if (strpos($headCommit, 'ref: ') === 0) {
  84                  // it is something like `ref: refs/heads/master`
  85                  $pathToHead = substr($headCommit, 5);
  86                  $headCommit = trim(file_get_contents(DOKU_INC . '.git/' . $pathToHead));
  87              }
  88              $subDir = substr($headCommit, 0, 2);
  89              $fileName = substr($headCommit, 2);
  90              $gitCommitObject = DOKU_INC . ".git/objects/$subDir/$fileName";
  91              if (file_exists($gitCommitObject) && method_exists(zlib_decode)) {
  92                  $commit = zlib_decode(file_get_contents($gitCommitObject));
  93                  $committerLine = explode("\n", $commit)[3];
  94                  $committerData = explode(' ', $committerLine);
  95                  end($committerData);
  96                  $ts = prev($committerData);
  97                  if ($ts && $date = date('Y-m-d', $ts)) {
  98                      $version['date'] = $date;
  99                  }
 100              }
 101          }
 102      }else{
 103          global $updateVersion;
 104          $version['date'] = 'update version '.$updateVersion;
 105          $version['type'] = 'snapshot?';
 106      }
 107      return $version;
 108  }
 109  
 110  /**
 111   * Return DokuWiki's version (as a string)
 112   *
 113   * @author Anika Henke <anika@selfthinker.org>
 114   */
 115  function getVersion(){
 116      $version = getVersionData();
 117      return $version['type'].' '.$version['date'];
 118  }
 119  
 120  /**
 121   * Run a few sanity checks
 122   *
 123   * @author Andreas Gohr <andi@splitbrain.org>
 124   */
 125  function check(){
 126      global $conf;
 127      global $INFO;
 128      /* @var Input $INPUT */
 129      global $INPUT;
 130  
 131      if ($INFO['isadmin'] || $INFO['ismanager']){
 132          msg('DokuWiki version: '.getVersion(),1);
 133  
 134          if(version_compare(phpversion(),'7.2.0','<')){
 135              msg('Your PHP version is too old ('.phpversion().' vs. 7.2+ needed)',-1);
 136          }else{
 137              msg('PHP version '.phpversion(),1);
 138          }
 139      } else {
 140          if(version_compare(phpversion(),'7.2.0','<')){
 141              msg('Your PHP version is too old',-1);
 142          }
 143      }
 144  
 145      $mem = (int) php_to_byte(ini_get('memory_limit'));
 146      if($mem){
 147          if ($mem === -1) {
 148              msg('PHP memory is unlimited', 1);
 149          } else if ($mem < 16777216) {
 150              msg('PHP is limited to less than 16MB RAM (' . filesize_h($mem) . ').
 151              Increase memory_limit in php.ini', -1);
 152          } else if ($mem < 20971520) {
 153              msg('PHP is limited to less than 20MB RAM (' . filesize_h($mem) . '),
 154                  you might encounter problems with bigger pages. Increase memory_limit in php.ini', -1);
 155          } else if ($mem < 33554432) {
 156              msg('PHP is limited to less than 32MB RAM (' . filesize_h($mem) . '),
 157                  but that should be enough in most cases. If not, increase memory_limit in php.ini', 0);
 158          } else {
 159              msg('More than 32MB RAM (' . filesize_h($mem) . ') available.', 1);
 160          }
 161      }
 162  
 163      if(is_writable($conf['changelog'])){
 164          msg('Changelog is writable',1);
 165      }else{
 166          if (file_exists($conf['changelog'])) {
 167              msg('Changelog is not writable',-1);
 168          }
 169      }
 170  
 171      if (isset($conf['changelog_old']) && file_exists($conf['changelog_old'])) {
 172          msg('Old changelog exists', 0);
 173      }
 174  
 175      if (file_exists($conf['changelog'].'_failed')) {
 176          msg('Importing old changelog failed', -1);
 177      } else if (file_exists($conf['changelog'].'_importing')) {
 178          msg('Importing old changelog now.', 0);
 179      } else if (file_exists($conf['changelog'].'_import_ok')) {
 180          msg('Old changelog imported', 1);
 181          if (!plugin_isdisabled('importoldchangelog')) {
 182              msg('Importoldchangelog plugin not disabled after import', -1);
 183          }
 184      }
 185  
 186      if(is_writable(DOKU_CONF)){
 187          msg('conf directory is writable',1);
 188      }else{
 189          msg('conf directory is not writable',-1);
 190      }
 191  
 192      if($conf['authtype'] == 'plain'){
 193          global $config_cascade;
 194          if(is_writable($config_cascade['plainauth.users']['default'])){
 195              msg('conf/users.auth.php is writable',1);
 196          }else{
 197              msg('conf/users.auth.php is not writable',0);
 198          }
 199      }
 200  
 201      if(function_exists('mb_strpos')){
 202          if(defined('UTF8_NOMBSTRING')){
 203              msg('mb_string extension is available but will not be used',0);
 204          }else{
 205              msg('mb_string extension is available and will be used',1);
 206              if(ini_get('mbstring.func_overload') != 0){
 207                  msg('mb_string function overloading is enabled, this will cause problems and should be disabled',-1);
 208              }
 209          }
 210      }else{
 211          msg('mb_string extension not available - PHP only replacements will be used',0);
 212      }
 213  
 214      if (!UTF8_PREGSUPPORT) {
 215          msg('PHP is missing UTF-8 support in Perl-Compatible Regular Expressions (PCRE)', -1);
 216      }
 217      if (!UTF8_PROPERTYSUPPORT) {
 218          msg('PHP is missing Unicode properties support in Perl-Compatible Regular Expressions (PCRE)', -1);
 219      }
 220  
 221      $loc = setlocale(LC_ALL, 0);
 222      if(!$loc){
 223          msg('No valid locale is set for your PHP setup. You should fix this',-1);
 224      }elseif(stripos($loc,'utf') === false){
 225          msg('Your locale <code>'.hsc($loc).'</code> seems not to be a UTF-8 locale,
 226               you should fix this if you encounter problems.',0);
 227      }else{
 228          msg('Valid locale '.hsc($loc).' found.', 1);
 229      }
 230  
 231      if($conf['allowdebug']){
 232          msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
 233      }else{
 234          msg('Debugging support is disabled',1);
 235      }
 236  
 237      if($INFO['userinfo']['name']){
 238          msg('You are currently logged in as '.$INPUT->server->str('REMOTE_USER').' ('.$INFO['userinfo']['name'].')',0);
 239          msg('You are part of the groups '.implode(', ', $INFO['userinfo']['grps']),0);
 240      }else{
 241          msg('You are currently not logged in',0);
 242      }
 243  
 244      msg('Your current permission for this page is '.$INFO['perm'],0);
 245  
 246      if (file_exists($INFO['filepath']) && is_writable($INFO['filepath'])) {
 247          msg('The current page is writable by the webserver', 1);
 248      } elseif (!file_exists($INFO['filepath']) && is_writable(dirname($INFO['filepath']))) {
 249          msg('The current page can be created by the webserver', 1);
 250      } else {
 251          msg('The current page is not writable by the webserver', -1);
 252      }
 253  
 254      if ($INFO['writable']) {
 255          msg('The current page is writable by you', 1);
 256      } else {
 257          msg('The current page is not writable by you', -1);
 258      }
 259  
 260      // Check for corrupted search index
 261      $lengths = idx_listIndexLengths();
 262      $index_corrupted = false;
 263      foreach ($lengths as $length) {
 264          if (count(idx_getIndex('w', $length)) != count(idx_getIndex('i', $length))) {
 265              $index_corrupted = true;
 266              break;
 267          }
 268      }
 269  
 270      foreach (idx_getIndex('metadata', '') as $index) {
 271          if (count(idx_getIndex($index.'_w', '')) != count(idx_getIndex($index.'_i', ''))) {
 272              $index_corrupted = true;
 273              break;
 274          }
 275      }
 276  
 277      if($index_corrupted) {
 278          msg(
 279              'The search index is corrupted. It might produce wrong results and most
 280                  probably needs to be rebuilt. See
 281                  <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
 282                  for ways to rebuild the search index.', -1
 283          );
 284      } elseif(!empty($lengths)) {
 285          msg('The search index seems to be working', 1);
 286      } else {
 287          msg(
 288              'The search index is empty. See
 289                  <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
 290                  for help on how to fix the search index. If the default indexer
 291                  isn\'t used or the wiki is actually empty this is normal.'
 292          );
 293      }
 294  
 295      // rough time check
 296      $http = new DokuHTTPClient();
 297      $http->max_redirect = 0;
 298      $http->timeout = 3;
 299      $http->sendRequest('http://www.dokuwiki.org', '', 'HEAD');
 300      $now = time();
 301      if(isset($http->resp_headers['date'])) {
 302          $time = strtotime($http->resp_headers['date']);
 303          $diff = $time - $now;
 304  
 305          if(abs($diff) < 4) {
 306              msg("Server time seems to be okay. Diff: {$diff}s", 1);
 307          } else {
 308              msg("Your server's clock seems to be out of sync!
 309                   Consider configuring a sync with a NTP server.  Diff: {$diff}s");
 310          }
 311      }
 312  
 313  }
 314  
 315  /**
 316   * Display a message to the user
 317   *
 318   * If HTTP headers were not sent yet the message is added
 319   * to the global message array else it's printed directly
 320   * using html_msgarea()
 321   *
 322   * Triggers INFOUTIL_MSG_SHOW
 323   *
 324   * @see    html_msgarea()
 325   * @param string $message
 326   * @param int    $lvl   -1 = error, 0 = info, 1 = success, 2 = notify
 327   * @param string $line  line number
 328   * @param string $file  file number
 329   * @param int    $allow who's allowed to see the message, see MSG_* constants
 330   */
 331  function msg($message,$lvl=0,$line='',$file='',$allow=MSG_PUBLIC){
 332      global $MSG, $MSG_shown;
 333      static $errors = [
 334          -1 => 'error',
 335          0 => 'info',
 336          1 => 'success',
 337          2 => 'notify',
 338      ];
 339  
 340      $msgdata = [
 341          'msg' => $message,
 342          'lvl' => $errors[$lvl],
 343          'allow' => $allow,
 344          'line' => $line,
 345          'file' => $file,
 346      ];
 347  
 348      $evt = new \dokuwiki\Extension\Event('INFOUTIL_MSG_SHOW', $msgdata);
 349      if ($evt->advise_before()) {
 350          /* Show msg normally - event could suppress message show */
 351          if($msgdata['line'] || $msgdata['file']) {
 352              $basename = \dokuwiki\Utf8\PhpString::basename($msgdata['file']);
 353              $msgdata['msg'] .=' ['.$basename.':'.$msgdata['line'].']';
 354          }
 355  
 356          if(!isset($MSG)) $MSG = array();
 357          $MSG[] = $msgdata;
 358          if(isset($MSG_shown) || headers_sent()){
 359              if(function_exists('html_msgarea')){
 360                  html_msgarea();
 361              }else{
 362                  print "ERROR(".$msgdata['lvl'].") ".$msgdata['msg']."\n";
 363              }
 364              unset($GLOBALS['MSG']);
 365          }
 366      }
 367      $evt->advise_after();
 368      unset($evt);
 369  }
 370  /**
 371   * Determine whether the current user is allowed to view the message
 372   * in the $msg data structure
 373   *
 374   * @param  $msg   array    dokuwiki msg structure
 375   *                         msg   => string, the message
 376   *                         lvl   => int, level of the message (see msg() function)
 377   *                         allow => int, flag used to determine who is allowed to see the message
 378   *                                       see MSG_* constants
 379   * @return bool
 380   */
 381  function info_msg_allowed($msg){
 382      global $INFO, $auth;
 383  
 384      // is the message public? - everyone and anyone can see it
 385      if (empty($msg['allow']) || ($msg['allow'] == MSG_PUBLIC)) return true;
 386  
 387      // restricted msg, but no authentication
 388      if (empty($auth)) return false;
 389  
 390      switch ($msg['allow']){
 391          case MSG_USERS_ONLY:
 392              return !empty($INFO['userinfo']);
 393  
 394          case MSG_MANAGERS_ONLY:
 395              return $INFO['ismanager'];
 396  
 397          case MSG_ADMINS_ONLY:
 398              return $INFO['isadmin'];
 399  
 400          default:
 401              trigger_error('invalid msg allow restriction.  msg="'.$msg['msg'].'" allow='.$msg['allow'].'"',
 402                            E_USER_WARNING);
 403              return $INFO['isadmin'];
 404      }
 405  
 406      return false;
 407  }
 408  
 409  /**
 410   * print debug messages
 411   *
 412   * little function to print the content of a var
 413   *
 414   * @author Andreas Gohr <andi@splitbrain.org>
 415   *
 416   * @param string $msg
 417   * @param bool $hidden
 418   */
 419  function dbg($msg,$hidden=false){
 420      if($hidden){
 421          echo "<!--\n";
 422          print_r($msg);
 423          echo "\n-->";
 424      }else{
 425          echo '<pre class="dbg">';
 426          echo hsc(print_r($msg,true));
 427          echo '</pre>';
 428      }
 429  }
 430  
 431  /**
 432   * Print info to a log file
 433   *
 434   * @author Andreas Gohr <andi@splitbrain.org>
 435   *
 436   * @param string $msg
 437   * @param string $header
 438   */
 439  function dbglog($msg,$header=''){
 440      global $conf;
 441      /* @var Input $INPUT */
 442      global $INPUT;
 443  
 444      // The debug log isn't automatically cleaned thus only write it when
 445      // debugging has been enabled by the user.
 446      if($conf['allowdebug'] !== 1) return;
 447      if(is_object($msg) || is_array($msg)){
 448          $msg = print_r($msg,true);
 449      }
 450  
 451      if($header) $msg = "$header\n$msg";
 452  
 453      $file = $conf['cachedir'].'/debug.log';
 454      $fh = fopen($file,'a');
 455      if($fh){
 456          fwrite($fh,date('H:i:s ').$INPUT->server->str('REMOTE_ADDR').': '.$msg."\n");
 457          fclose($fh);
 458      }
 459  }
 460  
 461  /**
 462   * Log accesses to deprecated fucntions to the debug log
 463   *
 464   * @param string $alternative The function or method that should be used instead
 465   * @triggers INFO_DEPRECATION_LOG
 466   */
 467  function dbg_deprecated($alternative = '') {
 468      \dokuwiki\Debug\DebugHelper::dbgDeprecatedFunction($alternative, 2);
 469  }
 470  
 471  /**
 472   * Print a reversed, prettyprinted backtrace
 473   *
 474   * @author Gary Owen <gary_owen@bigfoot.com>
 475   */
 476  function dbg_backtrace(){
 477      // Get backtrace
 478      $backtrace = debug_backtrace();
 479  
 480      // Unset call to debug_print_backtrace
 481      array_shift($backtrace);
 482  
 483      // Iterate backtrace
 484      $calls = array();
 485      $depth = count($backtrace) - 1;
 486      foreach ($backtrace as $i => $call) {
 487          $location = $call['file'] . ':' . $call['line'];
 488          $function = (isset($call['class'])) ?
 489              $call['class'] . $call['type'] . $call['function'] : $call['function'];
 490  
 491          $params = array();
 492          if (isset($call['args'])){
 493              foreach($call['args'] as $arg){
 494                  if(is_object($arg)){
 495                      $params[] = '[Object '.get_class($arg).']';
 496                  }elseif(is_array($arg)){
 497                      $params[] = '[Array]';
 498                  }elseif(is_null($arg)){
 499                      $params[] = '[NULL]';
 500                  }else{
 501                      $params[] = (string) '"'.$arg.'"';
 502                  }
 503              }
 504          }
 505          $params = implode(', ',$params);
 506  
 507          $calls[$depth - $i] = sprintf('%s(%s) called at %s',
 508                  $function,
 509                  str_replace("\n", '\n', $params),
 510                  $location);
 511      }
 512      ksort($calls);
 513  
 514      return implode("\n", $calls);
 515  }
 516  
 517  /**
 518   * Remove all data from an array where the key seems to point to sensitive data
 519   *
 520   * This is used to remove passwords, mail addresses and similar data from the
 521   * debug output
 522   *
 523   * @author Andreas Gohr <andi@splitbrain.org>
 524   *
 525   * @param array $data
 526   */
 527  function debug_guard(&$data){
 528      foreach($data as $key => $value){
 529          if(preg_match('/(notify|pass|auth|secret|ftp|userinfo|token|buid|mail|proxy)/i',$key)){
 530              $data[$key] = '***';
 531              continue;
 532          }
 533          if(is_array($value)) debug_guard($data[$key]);
 534      }
 535  }