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