1<?php 2/** 3 * @package tikiwiki 4 */ 5// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 6// 7// All Rights Reserved. See copyright.txt for details and a complete list of authors. 8// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 9// $Id$ 10/* 11About the design: 12tiki-check.php is designed to run in 2 modes 131) Regular mode. From inside Tiki, in Admin | General 142) Stand-alone mode. Used to check a server pre-Tiki installation, by copying (only) tiki-check.php onto the server and pointing your browser to it. 15tiki-check.php should not crash but rather avoid running tests which lead to tiki-check crashes. 16*/ 17 18use Tiki\Lib\Alchemy\AlchemyLib; 19use Tiki\Package\ComposerManager; 20 21// TODO : Create sane 3rd mode for Monitoring Software like Nagios, Icinga, Shinken 22// * needs authentication, if not standalone 23isset($_REQUEST['nagios']) ? $nagios = true : $nagios = false; 24file_exists('tiki-check.php.lock') ? $locked = true : $locked = false; 25$font = 'lib/captcha/DejaVuSansMono.ttf'; 26 27$inputConfiguration = array( 28 array( 29 'staticKeyFilters' => array( 30 'dbhost' => 'text', 31 'dbuser' => 'text', 32 'dbpass' => 'text', 33 'email_test_to' => 'email', 34 ), 35 ), 36); 37 38// reflector for SefURL check 39if (isset($_REQUEST['tiki-check-ping'])) { 40 die('pong:' . (int)$_REQUEST['tiki-check-ping']); 41} 42 43 44function checkOPCacheCompatibility() 45{ 46 return ! ((version_compare(PHP_VERSION, '7.1.0', '>=') && version_compare(PHP_VERSION, '7.2.0', '<')) //7.1.x 47 || (version_compare(PHP_VERSION, '7.2.0', '>=') && version_compare(PHP_VERSION, '7.2.19', '<')) // >= 7.2.0 < 7.2.19 48 || (version_compare(PHP_VERSION, '7.3.0', '>=') && version_compare(PHP_VERSION, '7.3.6', '<'))); // >= 7.3.0 < 7.3.6 49} 50 51if (file_exists('./db/local.php') && file_exists('./templates/tiki-check.tpl')) { 52 $standalone = false; 53 require_once('tiki-setup.php'); 54 // TODO : Proper authentication 55 $access->check_permission('tiki_p_admin'); 56 57 // This page is an admin tool usually used in the early stages of setting up Tiki, before layout considerations. 58 // Restricting the width is contrary to its purpose. 59 $prefs['feature_fixed_width'] = 'n'; 60} else { 61 $standalone = true; 62 $render = ""; 63 64 /** 65 * @param $string 66 * @return mixed 67 */ 68 function tra($string) 69 { 70 return $string; 71 } 72 73 function tr($string) 74 { 75 return tra($string); 76 } 77 78 79 /** 80 * @param $var 81 * @param $style 82 */ 83 function renderTable($var, $style = "") 84 { 85 global $render; 86 $morestyle = ""; 87 if ($style == "wrap") { 88 $morestyle = "overflow-wrap: anywhere;"; 89 } 90 if (is_array($var)) { 91 $render .= '<table style="border:2px solid grey;' . $morestyle . '">'; 92 foreach ($var as $key => $value) { 93 $render .= '<tr style="border:1px solid">'; 94 $render .= '<td style="border:1px black;padding:5px;white-space:nowrap;">'; 95 $render .= $key; 96 $render .= "</td>"; 97 $iNbCol = 0; 98 foreach ($var[$key] as $key2 => $value2) { 99 $render .= '<td style="border:1px solid;'; 100 if ($iNbCol != count(array_keys($var[$key])) - 1) { 101 $render .= 'text-align: center;white-space:nowrap;'; 102 } 103 $render .= '"><span class="'; 104 switch ($value2) { 105 case 'good': 106 case 'safe': 107 case 'unsure': 108 case 'bad': 109 case 'risky': 110 case 'info': 111 $render .= "button $value2"; 112 break; 113 } 114 $render .= '">' . $value2 . '</span></td>'; 115 $iNbCol++; 116 } 117 $render .= '</tr>'; 118 } 119 $render .= '</table>'; 120 } else { 121 $render .= 'Nothing to display.'; 122 } 123 } 124} 125 126// Get PHP properties and check them 127$php_properties = false; 128 129// Check error reporting level 130$e = error_reporting(); 131$d = ini_get('display_errors'); 132$l = ini_get('log_errors'); 133if ($l) { 134 if (! $d) { 135 $php_properties['Error logging'] = array( 136 'fitness' => tra('info'), 137 'setting' => 'Enabled', 138 'message' => tra('Errors will be logged, since log_errors is enabled. Also, display_errors is disabled. This is good practice for a production site, to log the errors instead of displaying them.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 139 ); 140 } else { 141 $php_properties['Error logging'] = array( 142 'fitness' => tra('info'), 143 'setting' => 'Enabled', 144 'message' => tra('Errors will be logged, since log_errors is enabled, but display_errors is also enabled. Good practice, especially for a production site, is to log all errors instead of displaying them.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 145 ); 146 } 147} else { 148 $php_properties['Error logging'] = array( 149 'fitness' => tra('info'), 150 'setting' => 'Full', 151 'message' => tra('Errors will not be logged, since log_errors is not enabled. Good practice, especially for a production site, is to log all errors.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 152 ); 153} 154if ($e == 0) { 155 if ($d != 1) { 156 $php_properties['Error reporting'] = array( 157 'fitness' => tra('info'), 158 'setting' => 'Disabled', 159 'message' => tra('Errors will not be reported, because error_reporting and display_errors are both turned off. This may be appropriate for a production site but, if any problems occur, enable these in php.ini to get more information.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 160 ); 161 } else { 162 $php_properties['Error reporting'] = array( 163 'fitness' => tra('info'), 164 'setting' => 'Disabled', 165 'message' => tra('No errors will be reported, although display_errors is On, because the error_reporting level is set to 0. This may be appropriate for a production site but, in if any problems occur, raise the value in php.ini to get more information.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 166 ); 167 } 168} elseif ($e > 0 && $e < 32767) { 169 if ($d != 1) { 170 $php_properties['Error reporting'] = array( 171 'fitness' => tra('info'), 172 'setting' => 'Disabled', 173 'message' => tra('No errors will be reported, because display_errors is turned off. This may be appropriate for a production site but, in any problems occur, enable it in php.ini to get more information. The error_reporting level is reasonable at ' . $e . '.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 174 ); 175 } else { 176 $php_properties['Error reporting'] = array( 177 'fitness' => tra('info'), 178 'setting' => 'Partly', 179 'message' => tra('Not all errors will be reported as the error_reporting level is at ' . $e . '. ' . 'This is not necessarily a bad thing (and it may be appropriate for a production site) as critical errors will be reported, but sometimes it may be useful to get more information. Check the error_reporting level in php.ini if any problems are occurring.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 180 ); 181 } 182} else { 183 if ($d != 1) { 184 $php_properties['Error reporting'] = array( 185 'fitness' => tra('info'), 186 'setting' => 'Disabled', 187 'message' => tra('No errors will be reported although the error_reporting level is all the way up at ' . $e . ', because display_errors is off. This may be appropriate for a production site but, in case of problems, enable it in php.ini to get more information.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 188 ); 189 } else { 190 $php_properties['Error reporting'] = array( 191 'fitness' => tra('info'), 192 'setting' => 'Full', 193 'message' => tra('All errors will be reported as the error_reporting level is all the way up at ' . $e . ' and display_errors is on. This is good because, in case of problems, the error reports usually contain useful information.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 194 ); 195 } 196} 197 198// Now we can raise our error_reporting to make sure we get all errors 199// This is especially important as we can't use proper exception handling with PDO as we need to be PHP 4 compatible 200error_reporting(-1); 201 202// Check if ini_set works 203if (function_exists('ini_set')) { 204 $php_properties['ini_set'] = array( 205 'fitness' => tra('good'), 206 'setting' => 'Enabled', 207 'message' => tra('ini_set is used in some places to accommodate special needs of some Tiki features.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 208 ); 209 // As ini_set is available, use it for PDO error reporting 210 ini_set('display_errors', '1'); 211} else { 212 $php_properties['ini_set'] = array( 213 'fitness' => tra('unsure'), 214 'setting' => 'Disabled', 215 'message' => tra('ini_set is used in some places to accommodate special needs of some Tiki features. Check disable_functions in your php.ini.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 216 ); 217} 218 219// First things first 220// If we don't have a DB-connection, some tests don't run 221$s = extension_loaded('pdo_mysql'); 222if ($s) { 223 $php_properties['DB Driver'] = array( 224 'fitness' => tra('good'), 225 'setting' => 'PDO', 226 'message' => tra('The PDO extension is the suggested database driver/abstraction layer.') 227 ); 228} elseif ($s = extension_loaded('mysqli')) { 229 $php_properties['DB Driver'] = array( 230 'fitness' => tra('unsure'), 231 'setting' => 'MySQLi', 232 'message' => tra('The recommended PDO database driver/abstraction layer cannot be found. The MySQLi driver is available, though, so the database connection will fall back to the AdoDB abstraction layer that is bundled with Tiki.') 233 ); 234} elseif (extension_loaded('mysql')) { 235 $php_properties['DB Driver'] = array( 236 'fitness' => tra('unsure'), 237 'setting' => 'MySQL', 238 'message' => tra('The recommended PDO database driver/abstraction layer cannot be found. The MySQL driver is available, though, so the database connection will fall back to the AdoDB abstraction layer that is bundled with Tiki.') 239 ); 240} else { 241 $php_properties['DB Driver'] = array( 242 'fitness' => tra('bad'), 243 'setting' => 'Not available', 244 'message' => tra('None of the supported database drivers (PDO/mysqli/mysql) is loaded. This prevents Tiki from functioning.') 245 ); 246} 247 248// Now connect to the DB and make all our connectivity methods work the same 249$connection = false; 250if ($standalone && ! $locked) { 251 if (empty($_POST['dbhost']) && ! ($php_properties['DB Driver']['setting'] == 'Not available')) { 252 $render .= <<<DBC 253<h2>Database credentials</h2> 254Couldn't connect to database, please provide valid credentials. 255<form method="post" action="{$_SERVER['SCRIPT_NAME']}"> 256 <p><label for="dbhost">Database host</label>: <input type="text" id="dbhost" name="dbhost" value="localhost" /></p> 257 <p><label for="dbuser">Database username</label>: <input type="text" id="dbuser" name="dbuser" /></p> 258 <p><label for="dbpass">Database password</label>: <input type="password" id="dbpass" name="dbpass" /></p> 259 <p><input type="submit" class="btn btn-primary btn-sm" value=" Connect " /></p> 260</form> 261DBC; 262 } else { 263 try { 264 switch ($php_properties['DB Driver']['setting']) { 265 case 'PDO': 266 // We don't do exception handling here to be PHP 4 compatible 267 $connection = new PDO('mysql:host=' . $_POST['dbhost'], $_POST['dbuser'], $_POST['dbpass']); 268 /** 269 * @param $query 270 * @param $connection 271 * @return mixed 272 */ 273 function query($query, $connection) 274 { 275 $result = $connection->query($query); 276 $return = $result->fetchAll(); 277 return($return); 278 } 279 break; 280 case 'MySQLi': 281 $error = false; 282 $connection = new mysqli($_POST['dbhost'], $_POST['dbuser'], $_POST['dbpass']); 283 $error = mysqli_connect_error(); 284 if (! empty($error)) { 285 $connection = false; 286 $render .= 'Couldn\'t connect to database: ' . htmlspecialchars($error); 287 } 288 /** 289 * @param $query 290 * @param $connection 291 * @return array 292 */ 293 function query($query, $connection) 294 { 295 $result = $connection->query($query); 296 $return = array(); 297 while ($row = $result->fetch_assoc()) { 298 $return[] = $row; 299 } 300 return($return); 301 } 302 break; 303 case 'MySQL': 304 $connection = mysql_connect($_POST['dbhost'], $_POST['dbuser'], $_POST['dbpass']); 305 if ($connection === false) { 306 $render .= 'Cannot connect to MySQL. Wrong credentials?'; 307 } 308 /** 309 * @param $query 310 * @param string $connection 311 * @return array 312 */ 313 function query($query, $connection = '') 314 { 315 $result = mysql_query($query); 316 $return = array(); 317 while ($row = mysql_fetch_array($result)) { 318 $return[] = $row; 319 } 320 return($return); 321 } 322 break; 323 } 324 } catch (Exception $e) { 325 $render .= 'Cannot connect to MySQL. Error: ' . htmlspecialchars($e->getMessage()); 326 } 327 } 328} else { 329 /** 330 * @param $query 331 * @return array 332 */ 333 function query($query) 334 { 335 global $tikilib; 336 $result = $tikilib->query($query); 337 $return = array(); 338 while ($row = $result->fetchRow()) { 339 $return[] = $row; 340 } 341 return($return); 342 } 343} 344 345// Basic Server environment 346$server_information['Operating System'] = array( 347 'value' => PHP_OS, 348); 349 350if (PHP_OS == 'Linux' && function_exists('exec')) { 351 exec('lsb_release -d', $output, $retval); 352 if ($retval == 0) { 353 $server_information['Release'] = array( 354 'value' => str_replace('Description:', '', $output[0]) 355 ); 356 # Check for FreeType fails without a font, i.e. standalone mode 357 # Using a URL as font source doesn't work on all PHP installs 358 # So let's try to gracefully fall back to some locally installed font at least on Linux 359 if (! file_exists($font)) { 360 $font = exec('find /usr/share/fonts/ -type f -name "*.ttf" | head -n 1', $output); 361 } 362 } else { 363 $server_information['Release'] = array( 364 'value' => tra('N/A') 365 ); 366 } 367} 368 369$server_information['Web Server'] = array( 370 'value' => $_SERVER['SERVER_SOFTWARE'] 371); 372 373$server_information['Server Signature']['value'] = ! empty($_SERVER['SERVER_SIGNATURE']) ? $_SERVER['SERVER_SIGNATURE'] : 'off'; 374 375// Free disk space 376if (function_exists('disk_free_space')) { 377 $bytes = @disk_free_space('.'); // this can fail on 32 bit systems with lots of disc space so suppress the possible warning 378 $si_prefix = array( 'B', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' ); 379 $base = 1024; 380 $class = min((int) log($bytes, $base), count($si_prefix) - 1); 381 $free_space = sprintf('%1.2f', $bytes / pow($base, $class)) . ' ' . $si_prefix[$class]; 382 if ($bytes === false) { 383 $server_properties['Disk Space'] = array( 384 'fitness' => 'unsure', 385 'setting' => tra('Unable to detect'), 386 'message' => tra('Cannot determine the size of this disk drive.') 387 ); 388 } elseif ($bytes < 200 * 1024 * 1024) { 389 $server_properties['Disk Space'] = array( 390 'fitness' => 'bad', 391 'setting' => $free_space, 392 'message' => tra('Less than 200MB of free disk space is available. Tiki will not fit in this amount of disk space.') 393 ); 394 } elseif ($bytes < 250 * 1024 * 1024) { 395 $server_properties['Disk Space'] = array( 396 'fitness' => 'unsure', 397 'setting' => $free_space, 398 'message' => tra('Less than 250MB of free disk space is available. This would be quite tight for a Tiki installation. Tiki needs disk space for compiled templates and uploaded files.') . ' ' . tra('When the disk space is filled, users, including administrators, will not be able to log in to Tiki.') . ' ' . tra('This test cannot reliably check for quotas, so be warned that if this server makes use of them, there might be less disk space available than reported.') 399 ); 400 } else { 401 $server_properties['Disk Space'] = array( 402 'fitness' => 'good', 403 'setting' => $free_space, 404 'message' => tra('More than 251MB of free disk space is available. Tiki will run smoothly, but there may be issues when the site grows (because of file uploads, for example).') . ' ' . tra('When the disk space is filled, users, including administrators, will not be able to log in to Tiki.') . ' ' . tra('This test cannot reliably check for quotas, so be warned that if this server makes use of them, there might be less disk space available than reported.') 405 ); 406 } 407} else { 408 $server_properties['Disk Space'] = array( 409 'fitness' => 'N/A', 410 'setting' => 'N/A', 411 'message' => tra('The PHP function disk_free_space is not available on your server, so the amount of available disk space can\'t be checked for.') 412 ); 413} 414 415// PHP Version 416if (version_compare(PHP_VERSION, '5.6.0', '<')) { 417 $php_properties['PHP version'] = array( 418 'fitness' => tra('unsure'), 419 'setting' => phpversion(), 420 'message' => 'This PHP version is somewhat old. Tiki 12.x LTS or 15.x LTS can be run, but not newer versions. Please see http://doc.tiki.org/Requirements for details.' 421 ); 422} elseif (version_compare(PHP_VERSION, '7.0.0', '<')) { 423 $php_properties['PHP version'] = array( 424 'fitness' => tra('unsure'), 425 'setting' => phpversion(), 426 'message' => 'This version of PHP is good, and Tiki versions between 15.x LTS and 18.x LTS will work fine on this version of PHP. Please see http://doc.tiki.org/Requirements for details.' 427 ); 428} elseif (version_compare(PHP_VERSION, '7.1.0', '<')) { 429 $php_properties['PHP version'] = array( 430 'fitness' => tra('good'), 431 'setting' => phpversion(), 432 'message' => 'This version of PHP is good, Tiki 18.x - Tiki 20 will work fine on this version of PHP. Please see http://doc.tiki.org/Requirements for details.' 433 ); 434} elseif (version_compare(PHP_VERSION, '7.2.0', '<')) { 435 $php_properties['PHP version'] = array( 436 'fitness' => tra('good'), 437 'setting' => phpversion(), 438 'message' => 'This version of PHP is good, Tiki 19.x - Tiki 21.x will work fine on this version of PHP. Please see http://doc.tiki.org/Requirements for details.' 439 ); 440} else { 441 $php_properties['PHP version'] = array( 442 'fitness' => tra('good'), 443 'setting' => phpversion(), 444 'message' => 'This version of PHP is recent. Versions 19.x and newer will work fine on this version of PHP. Please see http://doc.tiki.org/Requirements for details.' 445 ); 446} 447 448// Check PHP command line version 449if (function_exists('exec')) { 450 $cliSearchList = array('php', 'php56', 'php5.6', 'php5.6-cli'); 451 $isUnix = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? false : true; 452 $cliCommand = ''; 453 $cliVersion = ''; 454 foreach ($cliSearchList as $command) { 455 if ($isUnix) { 456 $output = exec('command -v ' . escapeshellarg($command) . ' 2>/dev/null'); 457 } else { 458 $output = exec('where ' . escapeshellarg($command . '.exe')); 459 } 460 if (! $output) { 461 continue; 462 } 463 464 $cliCommand = trim($output); 465 exec(escapeshellcmd(trim($cliCommand)) . ' --version', $output); 466 foreach ($output as $line) { 467 $parts = explode(' ', $line); 468 if ($parts[0] === 'PHP') { 469 $cliVersion = $parts[1]; 470 break; 471 } 472 } 473 break; 474 } 475 if ($cliCommand) { 476 if (phpversion() == $cliVersion) { 477 $php_properties['PHP CLI version'] = array( 478 'fitness' => tra('good'), 479 'setting' => $cliVersion, 480 'message' => 'The version of the command line executable of PHP (' . $cliCommand . ') is the same version as the web server version.', 481 ); 482 } else { 483 $php_properties['PHP CLI version'] = array( 484 'fitness' => tra('unsure'), 485 'setting' => $cliVersion, 486 'message' => 'The version of the command line executable of PHP (' . $cliCommand . ') is not the same as the web server version.', 487 ); 488 } 489 } else { 490 $php_properties['PHP CLI version'] = array( 491 'fitness' => tra('unsure'), 492 'setting' => '', 493 'message' => 'Unable to determine the command line executable for PHP.', 494 ); 495 } 496} 497 498// PHP Server API (SAPI) 499$s = php_sapi_name(); 500if (substr($s, 0, 3) == 'cgi') { 501 $php_properties['PHP Server API'] = array( 502 'fitness' => tra('info'), 503 'setting' => $s, 504 'message' => tra('PHP is being run as CGI. Feel free to use a threaded Apache MPM to increase performance.') 505 ); 506 507 $php_sapi_info = array( 508 'message' => tra('Looks like you are running PHP as FPM/CGI/FastCGI, you may be able to override some of your PHP configurations by add them to .user.ini files, see:'), 509 'link' => 'http://php.net/manual/en/configuration.file.per-user.php' 510 ); 511} elseif (substr($s, 0, 3) == 'fpm') { 512 $php_properties['PHP Server API'] = array( 513 'fitness' => tra('info'), 514 'setting' => $s, 515 'message' => tra('PHP is being run using FPM (Fastcgi Process Manager). Feel free to use a threaded Apache MPM to increase performance.') 516 ); 517 518 $php_sapi_info = array( 519 'message' => tra('Looks like you are running PHP as FPM/CGI/FastCGI, you may be able to override some of your PHP configurations by add them to .user.ini files, see:'), 520 'link' => 'http://php.net/manual/en/configuration.file.per-user.php' 521 ); 522} else { 523 if (substr($s, 0, 6) == 'apache') { 524 $php_sapi_info = array( 525 'message' => tra('Looks like you are running PHP as a module in Apache, you may be able to override some of your PHP configurations by add them to .htaccess files, see:'), 526 'link' => 'http://php.net/manual/en/configuration.changes.php#configuration.changes.apache' 527 ); 528 } 529 530 $php_properties['PHP Server API'] = array( 531 'fitness' => tra('info'), 532 'setting' => $s, 533 'message' => tra('PHP is not being run as CGI. Be aware that PHP is not thread-safe and you should not use a threaded Apache MPM (like worker).') 534 ); 535} 536 537// ByteCode Cache 538if (function_exists('apc_sma_info') && ini_get('apc.enabled')) { 539 $php_properties['ByteCode Cache'] = array( 540 'fitness' => tra('good'), 541 'setting' => 'APC', 542 'message' => tra('APC is being used as the ByteCode Cache, which increases performance if correctly configured. See Admin->Performance in the Tiki for more details.') 543 ); 544} elseif (function_exists('xcache_info') && ( ini_get('xcache.cacher') == '1' || ini_get('xcache.cacher') == 'On' )) { 545 $php_properties['ByteCode Cache'] = array( 546 'fitness' => tra('good'), 547 'setting' => 'xCache', 548 'message' => tra('xCache is being used as the ByteCode Cache, which increases performance if correctly configured. See Admin->Performance in the Tiki for more details.') 549 ); 550} elseif (function_exists('opcache_get_configuration') && (ini_get('opcache.enable') == 1 || ini_get('opcache.enable') == '1')) { 551 $message = tra('OPcache is being used as the ByteCode Cache, which increases performance if correctly configured. See Admin->Performance in the Tiki for more details.'); 552 $fitness = tra('good'); 553 if (! checkOPCacheCompatibility()) { 554 $message = tra('Some PHP versions may exhibit randomly issues with the OpCache leading to the server starting to fail to serve all PHP requests, your PHP version seems to 555 be affected, despite the performance penalty, we would recommend disabling the OpCache if you experience random crashes.'); 556 $fitness = tra('unsure'); 557 } 558 $php_properties['ByteCode Cache'] = array( 559 'fitness' => $fitness, 560 'setting' => 'OPcache', 561 'message' => $message 562 ); 563} elseif (function_exists('wincache_fcache_fileinfo')) { 564 // Determine if version 1 or 2 is used. Version 2 does not support ocache 565 566 if (function_exists('wincache_ocache_fileinfo')) { 567 // Wincache version 1 568 if (ini_get('wincache.ocenabled') == '1') { 569 $sapi_type = php_sapi_name(); 570 if ($sapi_type == 'cgi-fcgi') { 571 $php_properties['ByteCode Cache'] = array( 572 'fitness' => tra('good'), 573 'setting' => 'WinCache', 574 'message' => tra('WinCache is being used as the ByteCode Cache, which increases performance if correctly configured. See Admin->Performance in the Tiki for more details.') 575 ); 576 } else { 577 $php_properties['ByteCode Cache'] = array( 578 'fitness' => tra('unsure'), 579 'setting' => 'WinCache', 580 'message' => tra('WinCache is being used as the ByteCode Cache, but the required CGI/FastCGI server API is apparently not being used.') 581 ); 582 } 583 } else { 584 no_cache_found(); 585 } 586 } else { 587 // Wincache version 2 or higher 588 if (ini_get('wincache.fcenabled') == '1') { 589 $sapi_type = php_sapi_name(); 590 if ($sapi_type == 'cgi-fcgi') { 591 $php_properties['ByteCode Cache'] = array( 592 'fitness' => tra('info'), 593 'setting' => 'WinCache', 594 'message' => tra('WinCache version 2 or higher is being used as the FileCache. It does not support a ByteCode Cache.') . ' ' . tra('It is recommended to use Zend opcode cache as the ByteCode Cache.') 595 ); 596 } else { 597 $php_properties['ByteCode Cache'] = array( 598 'fitness' => tra('unsure'), 599 'setting' => 'WinCache', 600 'message' => tra('WinCache version 2 or higher is being used as the FileCache, but the required CGI/FastCGI server API is apparently not being used.') . ' ' . tra('It is recommended to use Zend opcode cache as the ByteCode Cache.') 601 ); 602 } 603 } else { 604 no_cache_found(); 605 } 606 } 607} else { 608 no_cache_found(); 609} 610 611 612// memory_limit 613$memory_limit = ini_get('memory_limit'); 614$s = trim($memory_limit); 615$last = strtolower(substr($s, -1)); 616$s = substr($s, 0, -1); 617switch ($last) { 618 case 'g': 619 $s *= 1024; 620 // no break 621 case 'm': 622 $s *= 1024; 623 // no break 624 case 'k': 625 $s *= 1024; 626} 627if ($s >= 160 * 1024 * 1024) { 628 $php_properties['memory_limit'] = array( 629 'fitness' => tra('good'), 630 'setting' => $memory_limit, 631 'message' => tra('The memory_limit is at') . ' ' . $memory_limit . '. ' . tra('This is known to support smooth functioning even for bigger sites.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 632 ); 633} elseif ($s < 160 * 1024 * 1024 && $s > 127 * 1024 * 1024) { 634 $php_properties['memory_limit'] = array( 635 'fitness' => tra('unsure') , 636 'setting' => $memory_limit, 637 'message' => tra('The memory_limit is at') . ' ' . $memory_limit . '. ' . tra('This will normally work, but the site might run into problems when it grows.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 638 ); 639} elseif ($s == -1) { 640 $php_properties['memory_limit'] = array( 641 'fitness' => tra('unsure') , 642 'setting' => $memory_limit, 643 'message' => tra("The memory_limit is unlimited. This is not necessarily bad, but it's a good idea to limit this on productions servers in order to eliminate unexpectedly greedy scripts.") . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 644 ); 645} else { 646 $php_properties['memory_limit'] = array( 647 'fitness' => tra('bad'), 648 'setting' => $memory_limit, 649 'message' => tra('Your memory_limit is at') . ' ' . $memory_limit . '. ' . tra('This is known to cause issues! Ther memory_limit should be increased to at least 128M, which is the PHP default.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 650 ); 651} 652 653// session.save_handler 654$s = ini_get('session.save_handler'); 655if ($s != 'files') { 656 $php_properties['session.save_handler'] = array( 657 'fitness' => tra('unsure'), 658 'setting' => $s, 659 'message' => tra('The session.save_handler should be set to \'files\'.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 660 ); 661} else { 662 $php_properties['session.save_handler'] = array( 663 'fitness' => tra('good'), 664 'setting' => $s, 665 'message' => tra('Well set! The default setting of \'files\' is recommended for Tiki.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 666 ); 667} 668 669// session.save_path 670$s = ini_get('session.save_path'); 671if ($php_properties['session.save_handler']['setting'] == 'files') { 672 if (empty($s) || ! is_writable($s)) { 673 $php_properties['session.save_path'] = array( 674 'fitness' => tra('bad'), 675 'setting' => $s, 676 'message' => tra('The session.save_path must writable.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 677 ); 678 } else { 679 $php_properties['session.save_path'] = array( 680 'fitness' => tra('good'), 681 'setting' => $s, 682 'message' => tra('The session.save_path is writable.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 683 ); 684 } 685} else { 686 if (empty($s) || ! is_writable($s)) { 687 $php_properties['session.save_path'] = array( 688 'fitness' => tra('unsure'), 689 'setting' => $s, 690 'message' => tra('If you would be using the recommended session.save_handler setting of \'files\', the session.save_path would have to be writable. Currently it is not.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 691 ); 692 } else { 693 $php_properties['session.save_path'] = array( 694 'fitness' => tra('info'), 695 'setting' => $s, 696 'message' => tra('The session.save_path is writable.') . tra('It doesn\'t matter though, since your session.save_handler is not set to \'files\'.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 697 ); 698 } 699} 700 701$s = ini_get('session.gc_probability'); 702$php_properties['session.gc_probability'] = array( 703 'fitness' => tra('info'), 704 'setting' => $s, 705 'message' => tra('In conjunction with gc_divisor is used to manage probability that the gc (garbage collection) routine is started.') 706); 707 708$s = ini_get('session.gc_divisor'); 709$php_properties['session.gc_divisor'] = array( 710 'fitness' => tra('info'), 711 'setting' => $s, 712 'message' => tra('Coupled with session.gc_probability defines the probability that the gc (garbage collection) process is started on every session initialization. The probability is calculated by using gc_probability/gc_divisor, e.g. 1/100 means there is a 1% chance that the GC process starts on each request.') 713); 714 715$s = ini_get('session.gc_maxlifetime'); 716$php_properties['session.gc_maxlifetime'] = array( 717 'fitness' => tra('info'), 718 'setting' => $s . 's', 719 'message' => tra('Specifies the number of seconds after which data will be seen as \'garbage\' and potentially cleaned up. Garbage collection may occur during session start.') 720); 721 722// test session work 723@session_start(); 724 725if (empty($_SESSION['tiki-check'])) { 726 $php_properties['session'] = array( 727 'fitness' => tra('unsure'), 728 'setting' => tra('empty'), 729 'message' => tra('The session is empty. Try reloading the page and, if this message is displayed again, there may be a problem with the server setup.') 730 ); 731 $_SESSION['tiki-check'] = 1; 732} else { 733 $php_properties['session'] = array( 734 'fitness' => tra('good'), 735 'setting' => 'ok', 736 'message' => tra('This appears to work.') 737 ); 738} 739 740// zlib.output_compression 741$s = ini_get('zlib.output_compression'); 742if ($s) { 743 $php_properties['zlib.output_compression'] = array( 744 'fitness' => tra('info'), 745 'setting' => 'On', 746 'message' => tra('zlib output compression is turned on. This saves bandwidth. On the other hand, turning it off would reduce CPU usage. The appropriate choice can be made for this Tiki.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 747 ); 748} else { 749 $php_properties['zlib.output_compression'] = array( 750 'fitness' => tra('info'), 751 'setting' => 'Off', 752 'message' => tra('zlib output compression is turned off. This reduces CPU usage. On the other hand, turning it on would save bandwidth. The appropriate choice can be made for this Tiki.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 753 ); 754} 755 756// default_charset 757$s = ini_get('default_charset'); 758if (strtolower($s) == 'utf-8') { 759 $php_properties['default_charset'] = array( 760 'fitness' => tra('good'), 761 'setting' => $s, 762 'message' => tra('Correctly set! Tiki is fully UTF-8 and so should be this installation.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 763 ); 764} else { 765 $php_properties['default_charset'] = array( 766 'fitness' => tra('unsure'), 767 'setting' => $s, 768 'message' => tra('default_charset should be UTF-8 as Tiki is fully UTF-8. Please check the php.ini file.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 769 ); 770} 771 772// date.timezone 773$s = ini_get('date.timezone'); 774if (empty($s)) { 775 $php_properties['date.timezone'] = array( 776 'fitness' => tra('unsure'), 777 'setting' => $s, 778 'message' => tra('No time zone is set! While there are a number of fallbacks in PHP to determine the time zone, the only reliable solution is to set it explicitly in php.ini! Please check the value of date.timezone in php.ini.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 779 ); 780} else { 781 $php_properties['date.timezone'] = array( 782 'fitness' => tra('good'), 783 'setting' => $s, 784 'message' => tra('Well done! Having a time zone set protects the site from related errors.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 785 ); 786} 787 788// file_uploads 789$s = ini_get('file_uploads'); 790if ($s) { 791 $php_properties['file_uploads'] = array( 792 'fitness' => tra('good'), 793 'setting' => 'On', 794 'message' => tra('Files can be uploaded to Tiki.') 795 ); 796} else { 797 $php_properties['file_uploads'] = array( 798 'fitness' => tra('bad'), 799 'setting' => 'Off', 800 'message' => tra('Files cannot be uploaded to Tiki.') 801 ); 802} 803 804// max_execution_time 805$s = ini_get('max_execution_time'); 806if ($s >= 30 && $s <= 90) { 807 $php_properties['max_execution_time'] = array( 808 'fitness' => tra('good'), 809 'setting' => $s . 's', 810 'message' => tra('The max_execution_time is at') . ' ' . $s . '. ' . tra('This is a good value for production sites. If timeouts are experienced (such as when performing admin functions) this may need to be increased nevertheless.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 811 ); 812} elseif ($s == -1 || $s == 0) { 813 $php_properties['max_execution_time'] = array( 814 'fitness' => tra('unsure'), 815 'setting' => $s . 's', 816 'message' => tra('The max_execution_time is unlimited.') . ' ' . tra('This is not necessarily bad, but it\'s a good idea to limit this time on productions servers in order to eliminate unexpectedly long running scripts.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 817 ); 818} elseif ($s > 90) { 819 $php_properties['max_execution_time'] = array( 820 'fitness' => tra('unsure'), 821 'setting' => $s . 's', 822 'message' => tra('The max_execution_time is at') . ' ' . $s . '. ' . tra('This is not necessarily bad, but it\'s a good idea to limit this time on productions servers in order to eliminate unexpectedly long running scripts.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 823 ); 824} else { 825 $php_properties['max_execution_time'] = array( 826 'fitness' => tra('bad'), 827 'setting' => $s . 's', 828 'message' => tra('The max_execution_time is at') . ' ' . $s . '. ' . tra('It is likely that some scripts, such as admin functions, will not finish in this time! The max_execution_time should be incresed to at least 30s.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 829 ); 830} 831 832// max_input_time 833$s = ini_get('max_input_time'); 834if ($s >= 30 && $s <= 90) { 835 $php_properties['max_input_time'] = array( 836 'fitness' => tra('good'), 837 'setting' => $s . 's', 838 'message' => tra('The max_input_time is at') . ' ' . $s . '. ' . tra('This is a good value for production sites. If timeouts are experienced (such as when performing admin functions) this may need to be increased nevertheless.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 839 ); 840} elseif ($s == -1 || $s == 0) { 841 $php_properties['max_input_time'] = array( 842 'fitness' => tra('unsure'), 843 'setting' => $s . 's', 844 'message' => tra('The max_input_time is unlimited.') . ' ' . tra('This is not necessarily bad, but it\'s a good idea to limit this time on productions servers in order to eliminate unexpectedly long running scripts.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 845 ); 846} elseif ($s > 90) { 847 $php_properties['max_input_time'] = array( 848 'fitness' => tra('unsure'), 849 'setting' => $s . 's', 850 'message' => tra('The max_input_time is at') . ' ' . $s . '. ' . tra('This is not necessarily bad, but it\'s a good idea to limit this time on productions servers in order to eliminate unexpectedly long running scripts.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 851 ); 852} else { 853 $php_properties['max_input_time'] = array( 854 'fitness' => tra('bad'), 855 'setting' => $s . 's', 856 'message' => tra('The max_input_time is at') . ' ' . $s . '. ' . tra('It is likely that some scripts, such as admin functions, will not finish in this time! The max_input_time should be increased to at least 30 seconds.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 857 ); 858} 859// max_file_uploads 860$max_file_uploads = ini_get('max_file_uploads'); 861if ($max_file_uploads) { 862 $php_properties['max_file_uploads'] = array( 863 'fitness' => tra('info'), 864 'setting' => $max_file_uploads, 865 'message' => tra('The max_file_uploads is at') . ' ' . $max_file_uploads . '. ' . tra('This is the maximum number of files allowed to be uploaded simultaneously.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 866 ); 867} else { 868 $php_properties['max_file_uploads'] = array( 869 'fitness' => tra('info'), 870 'setting' => 'Not Available', 871 'message' => tra('The maximum number of files allowed to be uploaded is not available') 872 ); 873} 874// upload_max_filesize 875$upload_max_filesize = ini_get('upload_max_filesize'); 876$s = trim($upload_max_filesize); 877$last = strtolower(substr($s, -1)); 878$s = substr($s, 0, -1); 879switch ($last) { 880 case 'g': 881 $s *= 1024; 882 // no break 883 case 'm': 884 $s *= 1024; 885 // no break 886 case 'k': 887 $s *= 1024; 888} 889if ($s >= 8 * 1024 * 1024) { 890 $php_properties['upload_max_filesize'] = array( 891 'fitness' => tra('good'), 892 'setting' => $upload_max_filesize, 893 'message' => tra('The upload_max_filesize is at') . ' ' . $upload_max_filesize . '. ' . tra('Quite large files can be uploaded, but keep in mind to set the script timeouts accordingly.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 894 ); 895} elseif ($s == 0) { 896 $php_properties['upload_max_filesize'] = array( 897 'fitness' => tra('unsure'), 898 'setting' => $upload_max_filesize, 899 'message' => tra('The upload_max_filesize is at') . ' ' . $upload_max_filesize . '. ' . tra('Upload size is unlimited and this not advised. A user could mistakenly upload a very large file which could fill up the disk. This value should be set to accommodate the realistic needs of the site.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 900 ); 901} else { 902 $php_properties['upload_max_filesize'] = array( 903 'fitness' => tra('unsure'), 904 'setting' => $upload_max_filesize, 905 'message' => tra('The upload_max_filesize is at') . ' ' . $upload_max_filesize . '. ' . tra('This is not a bad amount, but be sure the level is high enough to accommodate the needs of the site.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 906 ); 907} 908 909// post_max_size 910$post_max_size = ini_get('post_max_size'); 911$s = trim($post_max_size); 912$last = strtolower(substr($s, -1)); 913$s = substr($s, 0, -1); 914switch ($last) { 915 case 'g': 916 $s *= 1024; 917 // no break 918 case 'm': 919 $s *= 1024; 920 // no break 921 case 'k': 922 $s *= 1024; 923} 924if ($s >= 8 * 1024 * 1024) { 925 $php_properties['post_max_size'] = array( 926 'fitness' => tra('good'), 927 'setting' => $post_max_size, 928 'message' => tra('The post_max_size is at') . ' ' . $post_max_size . '. ' . tra('Quite large files can be uploaded, but keep in mind to set the script timeouts accordingly.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 929 ); 930} else { 931 $php_properties['post_max_size'] = array( 932 'fitness' => tra('unsure'), 933 'setting' => $post_max_size, 934 'message' => tra('The post_max_size is at') . ' ' . $post_max_size . '. ' . tra('This is not a bad amount, but be sure the level is high enough to accommodate the needs of the site.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 935 ); 936} 937 938// PHP Extensions 939// fileinfo 940$s = extension_loaded('fileinfo'); 941if ($s) { 942 $php_properties['fileinfo'] = array( 943 'fitness' => tra('good'), 944 'setting' => 'Loaded', 945 'message' => tra("The fileinfo extension is needed for the 'Validate uploaded file content' preference.") 946 ); 947} else { 948 $php_properties['fileinfo'] = array( 949 'fitness' => tra('unsure'), 950 'setting' => 'Not available', 951 'message' => tra("The fileinfo extension is needed for the 'Validate uploaded file content' preference.") 952 ); 953} 954 955// intl 956$s = extension_loaded('intl'); 957if ($s) { 958 $php_properties['intl'] = array( 959 'fitness' => tra('good'), 960 'setting' => 'Loaded', 961 'message' => tra("The intl extension is required for Tiki 15 and newer.") 962 ); 963} else { 964 $php_properties['intl'] = array( 965 'fitness' => tra('unsure'), 966 'setting' => 'Not available', 967 'message' => tra("intl extension is preferred for Tiki 15 and newer. Because is not available, the filters for text will not be able to detect the language and will use a generic range of characters as letters.") 968 ); 969} 970 971// GD 972$s = extension_loaded('gd'); 973if ($s && function_exists('gd_info')) { 974 $gd_info = gd_info(); 975 $im = $ft = null; 976 if (function_exists('imagecreate')) { 977 $im = @imagecreate(110, 20); 978 } 979 if (function_exists('imageftbbox')) { 980 $ft = @imageftbbox(12, 0, $font, 'test'); 981 } 982 if ($im && $ft) { 983 $php_properties['gd'] = array( 984 'fitness' => tra('good'), 985 'setting' => $gd_info['GD Version'], 986 'message' => tra('The GD extension is needed for manipulation of images and for CAPTCHA images.') 987 ); 988 imagedestroy($im); 989 } elseif ($im) { 990 $php_properties['gd'] = array( 991 'fitness' => tra('unsure'), 992 'setting' => $gd_info['GD Version'], 993 'message' => tra('The GD extension is loaded, and Tiki can create images, but the FreeType extension is needed for CAPTCHA text generation.') 994 ); 995 imagedestroy($im); 996 } else { 997 $php_properties['gd'] = array( 998 'fitness' => tra('unsure'), 999 'setting' => 'Dysfunctional', 1000 'message' => tra('The GD extension is loaded, but Tiki is unable to create images. Please check your GD library configuration.') 1001 ); 1002 } 1003} else { 1004 $php_properties['gd'] = array( 1005 'fitness' => tra('bad'), 1006 'setting' => 'Not available', 1007 'message' => tra('The GD extension is needed for manipulation of images and for CAPTCHA images.') 1008 ); 1009} 1010 1011// Image Magick 1012$s = class_exists('Imagick'); 1013if ($s) { 1014 $image = new Imagick(); 1015 $image->newImage(100, 100, new ImagickPixel('red')); 1016 if ($image) { 1017 $php_properties['Image Magick'] = array( 1018 'fitness' => tra('good'), 1019 'setting' => 'Available', 1020 'message' => tra('ImageMagick is used as a fallback in case GD is not available.') 1021 ); 1022 $image->destroy(); 1023 } else { 1024 $php_properties['Image Magick'] = array( 1025 'fitness' => tra('unsure'), 1026 'setting' => 'Dysfunctional', 1027 'message' => tra('ImageMagick is used as a fallback in case GD is not available.') . tra('ImageMagick is available, but unable to create images. Please check your ImageMagick configuration.') 1028 ); 1029 } 1030} else { 1031 $php_properties['Image Magick'] = array( 1032 'fitness' => tra('info'), 1033 'setting' => 'Not Available', 1034 'message' => tra('ImageMagick is used as a fallback in case GD is not available.') 1035 ); 1036} 1037 1038// mbstring 1039$s = extension_loaded('mbstring'); 1040if ($s) { 1041 $func_overload = ini_get('mbstring.func_overload'); 1042 if ($func_overload == 0 && function_exists('mb_split')) { 1043 $php_properties['mbstring'] = array( 1044 'fitness' => tra('good'), 1045 'setting' => 'Loaded', 1046 'message' => tra('mbstring extension is needed for an UTF-8 compatible lower case filter, in the admin search for example.') 1047 ); 1048 } elseif ($func_overload != 0) { 1049 $php_properties['mbstring'] = array( 1050 'fitness' => tra('unsure'), 1051 'setting' => 'Badly configured', 1052 'message' => tra('mbstring extension is loaded, but mbstring.func_overload = ' . ' ' . $func_overload . '.' . ' ' . 'Tiki only works with mbstring.func_overload = 0. Please check the php.ini file.') 1053 ); 1054 } else { 1055 $php_properties['mbstring'] = array( 1056 'fitness' => tra('bad'), 1057 'setting' => 'Badly installed', 1058 'message' => tra('mbstring extension is loaded, but missing important functions such as mb_split(). Reinstall it with --enable-mbregex or ask your a server administrator to do it.') 1059 ); 1060 } 1061} else { 1062 $php_properties['mbstring'] = array( 1063 'fitness' => tra('bad'), 1064 'setting' => 'Not available', 1065 'message' => tra('mbstring extension is needed for an UTF-8 compatible lower case filter.') 1066 ); 1067} 1068 1069// calendar 1070$s = extension_loaded('calendar'); 1071if ($s) { 1072 $php_properties['calendar'] = array( 1073 'fitness' => tra('good'), 1074 'setting' => 'Loaded', 1075 'message' => tra('calendar extension is needed by Tiki.') 1076 ); 1077} else { 1078 $php_properties['calendar'] = array( 1079 'fitness' => tra('bad'), 1080 'setting' => 'Not available', 1081 'message' => tra('calendar extension is needed by Tiki.') . ' ' . tra('The calendar feature of Tiki will not function without this.') 1082 ); 1083} 1084 1085// ctype 1086$s = extension_loaded('ctype'); 1087if ($s) { 1088 $php_properties['ctype'] = array( 1089 'fitness' => tra('good'), 1090 'setting' => 'Loaded', 1091 'message' => tra('ctype extension is needed by Tiki.') 1092 ); 1093} else { 1094 $php_properties['ctype'] = array( 1095 'fitness' => tra('bad'), 1096 'setting' => 'Not available', 1097 'message' => tra('ctype extension is needed by Tiki.') 1098 ); 1099} 1100 1101// libxml 1102$s = extension_loaded('libxml'); 1103if ($s) { 1104 $php_properties['libxml'] = array( 1105 'fitness' => tra('good'), 1106 'setting' => 'Loaded', 1107 'message' => tra('This extension is needed for the dom extension (see below).') 1108 ); 1109} else { 1110 $php_properties['libxml'] = array( 1111 'fitness' => tra('bad'), 1112 'setting' => 'Not available', 1113 'message' => tra('This extension is needed for the dom extension (see below).') 1114 ); 1115} 1116 1117// dom (depends on libxml) 1118$s = extension_loaded('dom'); 1119if ($s) { 1120 $php_properties['dom'] = array( 1121 'fitness' => tra('good'), 1122 'setting' => 'Loaded', 1123 'message' => tra('This extension is needed by Tiki') 1124 ); 1125} else { 1126 $php_properties['dom'] = array( 1127 'fitness' => tra('bad'), 1128 'setting' => 'Not available', 1129 'message' => tra('This extension is needed by Tiki') 1130 ); 1131} 1132 1133$s = extension_loaded('ldap'); 1134if ($s) { 1135 $php_properties['LDAP'] = array( 1136 'fitness' => tra('good'), 1137 'setting' => 'Loaded', 1138 'message' => tra('This extension is needed to connect Tiki to an LDAP server. More info at: http://doc.tiki.org/LDAP ') 1139 ); 1140} else { 1141 $php_properties['LDAP'] = array( 1142 'fitness' => tra('info'), 1143 'setting' => 'Not available', 1144 'message' => tra('Tiki will not be able to connect to an LDAP server as the needed PHP extension is missing. More info at: http://doc.tiki.org/LDAP') 1145 ); 1146} 1147 1148$s = extension_loaded('memcached'); 1149if ($s) { 1150 $php_properties['memcached'] = array( 1151 'fitness' => tra('good'), 1152 'setting' => 'Loaded', 1153 'message' => tra('This extension can be used to speed up Tiki by saving sessions as well as wiki and forum data on a memcached server.') 1154 ); 1155} else { 1156 $php_properties['memcached'] = array( 1157 'fitness' => tra('info'), 1158 'setting' => 'Not available', 1159 'message' => tra('This extension can be used to speed up Tiki by saving sessions as well as wiki and forum data on a memcached server.') 1160 ); 1161} 1162 1163$s = extension_loaded('redis'); 1164if ($s) { 1165 $php_properties['redis'] = array( 1166 'fitness' => tra('good'), 1167 'setting' => 'Loaded', 1168 'message' => tra('This extension can be used to speed up Tiki by saving wiki and forum data on a redis server.') 1169 ); 1170} else { 1171 $php_properties['redis'] = array( 1172 'fitness' => tra('info'), 1173 'setting' => 'Not available', 1174 'message' => tra('This extension can be used to speed up Tiki by saving wiki and forum data on a redis server.') 1175 ); 1176} 1177 1178$s = extension_loaded('ssh2'); 1179if ($s) { 1180 $php_properties['SSH2'] = array( 1181 'fitness' => tra('good'), 1182 'setting' => 'Loaded', 1183 'message' => tra('This extension is needed for the show.tiki.org tracker field type, up to Tiki 17.') 1184 ); 1185} else { 1186 $php_properties['SSH2'] = array( 1187 'fitness' => tra('info'), 1188 'setting' => 'Not available', 1189 'message' => tra('This extension is needed for the show.tiki.org tracker field type, up to Tiki 17.') 1190 ); 1191} 1192 1193$s = extension_loaded('curl'); 1194if ($s) { 1195 $php_properties['curl'] = array( 1196 'fitness' => tra('good'), 1197 'setting' => 'Loaded', 1198 'message' => tra('This extension is required for H5P.') 1199 ); 1200} else { 1201 $php_properties['curl'] = array( 1202 'fitness' => tra('bad'), 1203 'setting' => 'Not available', 1204 'message' => tra('This extension is required for H5P.') 1205 ); 1206} 1207 1208$s = extension_loaded('json'); 1209if ($s) { 1210 $php_properties['json'] = array( 1211 'fitness' => tra('good'), 1212 'setting' => 'Loaded', 1213 'message' => tra('This extension is required for many features in Tiki.') 1214 ); 1215} else { 1216 $php_properties['json'] = array( 1217 'fitness' => tra('bad'), 1218 'setting' => 'Not available', 1219 'message' => tra('This extension is required for many features in Tiki.') 1220 ); 1221} 1222 1223/* 1224* If TortoiseSVN 1.7 is used, it uses an sqlite database to store the SVN info. sqlite3 extention needed to read svn info. 1225*/ 1226if (is_file('.svn/wc.db')) { 1227 // It's an TortoiseSVN 1.7+ installation 1228 $s = extension_loaded('sqlite3'); 1229 if ($s) { 1230 $php_properties['sqlite3'] = array( 1231 'fitness' => tra('good'), 1232 'setting' => 'Loaded', 1233 'message' => tra('This extension is used to interpret SVN information for TortoiseSVN 1.7 or higher.') 1234 ); 1235 } else { 1236 $php_properties['sqlite3'] = array( 1237 'fitness' => tra('unsure'), 1238 'setting' => 'Not available', 1239 'message' => tra('This extension is used to interpret SVN information for TortoiseSVN 1.7 or higher.') 1240 ); 1241 } 1242} 1243 1244 1245$s = extension_loaded('openssl'); 1246$msg = tra('Enable safe, encrypted storage of data such as passwords. Required for the User Encryption feature and improves encryption in other features, when available.'); 1247if ($s) { 1248 $php_properties['openssl'] = array( 1249 'fitness' => tra('good'), 1250 'setting' => 'Loaded', 1251 'message' => $msg 1252 ); 1253} else { 1254 $php_properties['openssl'] = array( 1255 'fitness' => tra('unsure'), 1256 'setting' => 'Not available', 1257 'message' => $msg 1258 ); 1259} 1260 1261 1262$s = extension_loaded('mcrypt'); 1263$msg = tra('MCrypt is abandonware and is being phased out. Starting in version 18, Tiki uses OpenSSL where it previously used MCrypt, except perhaps via third-party libraries.'); 1264if (! $standalone) { 1265 $msg .= ' ' . tra('Tiki still uses MCrypt to decrypt user data encrypted with MCrypt, when converting that data to OpenSSL.') . ' ' . tra('Please check the \'User Data Encryption\' section to see if there is user data encrypted with MCrypt.'); 1266 1267 //User Data Encryption MCrypt 1268 $usersWithMCrypt = check_userPreferencesMCrypt(); 1269} 1270if ($s) { 1271 $php_properties['mcrypt'] = array( 1272 'fitness' => tra('info'), 1273 'setting' => 'Loaded', 1274 'message' => $msg 1275 ); 1276} else { 1277 $php_properties['mcrypt'] = array( 1278 'fitness' => tra('info'), 1279 'setting' => 'Not available', 1280 'message' => $msg 1281 ); 1282} 1283 1284 1285if (! $standalone) { 1286 // check Zend captcha will work which depends on \Zend\Math\Rand 1287 $captcha = new Zend\Captcha\Dumb; 1288 $math_random = array( 1289 'fitness' => tra('good'), 1290 'setting' => 'Available', 1291 'message' => tra('Ability to generate random numbers, useful for example for CAPTCHA and other security features.'), 1292 ); 1293 try { 1294 $captchaId = $captcha->getId(); // simple test for missing random generator 1295 } catch (Exception $e) { 1296 $math_random['fitness'] = tra('unsure'); 1297 $math_random['setting'] = 'Not available'; 1298 } 1299 $php_properties['\Laminas\Math\Rand'] = $math_random; 1300} 1301 1302 1303$s = extension_loaded('iconv'); 1304$msg = tra('This extension is required and used frequently in validation functions invoked within Zend Framework.'); 1305if ($s) { 1306 $php_properties['iconv'] = array( 1307 'fitness' => tra('good'), 1308 'setting' => 'Loaded', 1309 'message' => $msg 1310 ); 1311} else { 1312 $php_properties['iconv'] = array( 1313 'fitness' => tra('bad'), 1314 'setting' => 'Not available', 1315 'message' => $msg 1316 ); 1317} 1318 1319// Check for existence of eval() 1320// eval() is a language construct and not a function 1321// so function_exists() doesn't work 1322$s = eval('return 42;'); 1323if ($s == 42) { 1324 $php_properties['eval()'] = array( 1325 'fitness' => tra('good'), 1326 'setting' => 'Available', 1327 'message' => tra('The eval() function is required by the Smarty templating engine.') 1328 ); 1329} else { 1330 $php_properties['eval()'] = array( 1331 'fitness' => tra('bad'), 1332 'setting' => 'Not available', 1333 'message' => tra('The eval() function is required by the Smarty templating engine.') . ' ' . tra('You will get "Please contact support about" messages instead of modules. eval() is most probably disabled via Suhosin.') 1334 ); 1335} 1336 1337// Zip Archive class 1338$s = class_exists('ZipArchive'); 1339if ($s) { 1340 $php_properties['ZipArchive class'] = array( 1341 'fitness' => tra('good'), 1342 'setting' => 'Available', 1343 'message' => tra('The ZipArchive class is needed for features such as XML Wiki Import/Export and PluginArchiveBuilder.') 1344 ); 1345} else { 1346 $php_properties['ZipArchive class'] = array( 1347 'fitness' => tra('unsure'), 1348 'setting' => 'Not Available', 1349 'message' => tra('The ZipArchive class is needed for features such as XML Wiki Import/Export and PluginArchiveBuilder.') 1350 ); 1351} 1352 1353// DateTime class 1354$s = class_exists('DateTime'); 1355if ($s) { 1356 $php_properties['DateTime class'] = array( 1357 'fitness' => tra('good'), 1358 'setting' => 'Available', 1359 'message' => tra('The DateTime class is needed for the WebDAV feature.') 1360 ); 1361} else { 1362 $php_properties['DateTime class'] = array( 1363 'fitness' => tra('unsure'), 1364 'setting' => 'Not Available', 1365 'message' => tra('The DateTime class is needed for the WebDAV feature.') 1366 ); 1367} 1368 1369// Xdebug 1370$has_xdebug = function_exists('xdebug_get_code_coverage') && is_array(xdebug_get_code_coverage()); 1371if ($has_xdebug) { 1372 $php_properties['Xdebug'] = array( 1373 'fitness' => tra('info'), 1374 'setting' => 'Loaded', 1375 'message' => tra('Xdebug can be very handy for a development server, but it might be better to disable it when on a production server.') 1376 ); 1377} else { 1378 $php_properties['Xdebug'] = array( 1379 'fitness' => tra('info'), 1380 'setting' => 'Not Available', 1381 'message' => tra('Xdebug can be very handy for a development server, but it might be better to disable it when on a production server.') 1382 ); 1383} 1384 1385// Get MySQL properties and check them 1386$mysql_properties = false; 1387$mysql_variables = false; 1388if ($connection || ! $standalone) { 1389 // MySQL version 1390 $query = 'SELECT VERSION();'; 1391 $result = query($query, $connection); 1392 $mysql_version = $result[0]['VERSION()']; 1393 $s = version_compare($mysql_version, '5.5.3', '>='); 1394 if ($s == true) { 1395 $mysql_properties['Version'] = array( 1396 'fitness' => tra('good'), 1397 'setting' => $mysql_version, 1398 'message' => tra('Tiki requires MariaDB >= 5.5 or MySQL >= 5.5.3') 1399 ); 1400 } else { 1401 $mysql_properties['Version'] = array( 1402 'fitness' => tra('bad'), 1403 'setting' => $mysql_version, 1404 'message' => tra('Tiki requires MariaDB >= 5.5 or MySQL >= 5.5.3') 1405 ); 1406 } 1407 1408 // max_allowed_packet 1409 $query = "SHOW VARIABLES LIKE 'max_allowed_packet'"; 1410 $result = query($query, $connection); 1411 $s = $result[0]['Value']; 1412 $max_allowed_packet = $s / 1024 / 1024; 1413 if ($s >= 8 * 1024 * 1024) { 1414 $mysql_properties['max_allowed_packet'] = array( 1415 'fitness' => tra('good'), 1416 'setting' => $max_allowed_packet . 'M', 1417 'message' => tra('The max_allowed_packet setting is at') . ' ' . $max_allowed_packet . 'M. ' . tra('Quite large files can be uploaded, but keep in mind to set the script timeouts accordingly.') . ' ' . tra('This limits the size of binary files that can be uploaded to Tiki, when storing files in the database. Please see: <a href="http://doc.tiki.org/File+Storage">file storage</a>.') 1418 ); 1419 } else { 1420 $mysql_properties['max_allowed_packet'] = array( 1421 'fitness' => tra('unsure'), 1422 'setting' => $max_allowed_packet . 'M', 1423 'message' => tra('The max_allowed_packet setting is at') . ' ' . $max_allowed_packet . 'M. ' . tra('This is not a bad amount, but be sure the level is high enough to accommodate the needs of the site.') . ' ' . tra('This limits the size of binary files that can be uploaded to Tiki, when storing files in the database. Please see: <a href="http://doc.tiki.org/File+Storage">file storage</a>.') 1424 ); 1425 } 1426 1427 // UTF-8 MB4 test (required for Tiki19+) 1428 $query = "SELECT COUNT(*) FROM `information_schema`.`character_sets` WHERE `character_set_name` = 'utf8mb4';"; 1429 $result = query($query, $connection); 1430 if (! empty($result[0]['COUNT(*)'])) { 1431 $mysql_properties['utf8mb4'] = array( 1432 'fitness' => tra('good'), 1433 'setting' => 'available', 1434 'message' => tra('Your database supports the utf8mb4 character set required in Tiki19 and above.') 1435 ); 1436 } else { 1437 $mysql_properties['utf8mb4'] = array( 1438 'fitness' => tra('bad'), 1439 'setting' => 'not available', 1440 'message' => tra('Your database does not support the utf8mb4 character set required in Tiki19 and above. You need to upgrade your mysql or mariadb installation.') 1441 ); 1442 } 1443 1444 // UTF-8 Charset 1445 // Tiki communication is done using UTF-8 MB4 (required for Tiki19+) 1446 $charset_types = "client connection database results server system"; 1447 foreach (explode(' ', $charset_types) as $type) { 1448 $query = "SHOW VARIABLES LIKE 'character_set_" . $type . "';"; 1449 $result = query($query, $connection); 1450 foreach ($result as $value) { 1451 if ($value['Value'] == 'utf8mb4') { 1452 $mysql_properties[$value['Variable_name']] = array( 1453 'fitness' => tra('good'), 1454 'setting' => $value['Value'], 1455 'message' => tra('Tiki is fully utf8mb4 and so should be every part of the stack.') 1456 ); 1457 } else { 1458 $mysql_properties[$value['Variable_name']] = array( 1459 'fitness' => tra('unsure'), 1460 'setting' => $value['Value'], 1461 'message' => tra('On a fresh install everything should be set to utf8mb4 to avoid unexpected results. For further information please see <a href="http://doc.tiki.org/Understanding+Encoding">Understanding Encoding</a>.') 1462 ); 1463 } 1464 } 1465 } 1466 // UTF-8 is correct for character_set_system 1467 // Because mysql does not allow any config to change this value, and character_set_system is overwritten by the other character_set_* variables anyway. They may change this default in later versions. 1468 $query = "SHOW VARIABLES LIKE 'character_set_system';"; 1469 $result = query($query, $connection); 1470 foreach ($result as $value) { 1471 if (substr($value['Value'], 0, 4) == 'utf8') { 1472 $mysql_properties[$value['Variable_name']] = array( 1473 'fitness' => tra('good'), 1474 'setting' => $value['Value'], 1475 'message' => tra('Tiki is fully utf8mb4 but some database underlying variables are set to utf8 by the database engine and cannot be modified.') 1476 ); 1477 } else { 1478 $mysql_properties[$value['Variable_name']] = array( 1479 'fitness' => tra('unsure'), 1480 'setting' => $value['Value'], 1481 'message' => tra('On a fresh install everything should be set to utf8mb4 or utf8 to avoid unexpected results. For further information please see <a href="http://doc.tiki.org/Understanding+Encoding">Understanding Encoding</a>.') 1482 ); 1483 } 1484 } 1485 // UTF-8 Collation 1486 $collation_types = "connection database server"; 1487 foreach (explode(' ', $collation_types) as $type) { 1488 $query = "SHOW VARIABLES LIKE 'collation_" . $type . "';"; 1489 $result = query($query, $connection); 1490 foreach ($result as $value) { 1491 if (substr($value['Value'], 0, 7) == 'utf8mb4') { 1492 $mysql_properties[$value['Variable_name']] = array( 1493 'fitness' => tra('good'), 1494 'setting' => $value['Value'], 1495 'message' => tra('Tiki is fully utf8mb4 and so should be every part of the stack. utf8mb4_unicode_ci is the default collation for Tiki.') 1496 ); 1497 } else { 1498 $mysql_properties[$value['Variable_name']] = array( 1499 'fitness' => tra('unsure'), 1500 'setting' => $value['Value'], 1501 'message' => tra('On a fresh install everything should be set to utf8mb4 to avoid unexpected results. utf8mb4_unicode_ci is the default collation for Tiki. For further information please see <a href="http://doc.tiki.org/Understanding+Encoding">Understanding Encoding</a>.') 1502 ); 1503 } 1504 } 1505 } 1506 1507 // slow_query_log 1508 $query = "SHOW VARIABLES LIKE 'slow_query_log'"; 1509 $result = query($query, $connection); 1510 $s = $result[0]['Value']; 1511 if ($s == 'OFF') { 1512 $mysql_properties['slow_query_log'] = array( 1513 'fitness' => tra('info'), 1514 'setting' => $s, 1515 'message' => tra('MySQL doesn\'t log slow queries. If performance issues are noticed, this could be enabled, but keep in mind that the logging itself slows MySQL down.') 1516 ); 1517 } else { 1518 $mysql_properties['slow_query_log'] = array( 1519 'fitness' => tra('info'), 1520 'setting' => $s, 1521 'message' => tra('MySQL logs slow queries. If no performance issues are noticed, this should be disabled on a production site as it slows MySQL down.') 1522 ); 1523 } 1524 1525 // MySQL SSL 1526 $query = 'show variables like "have_ssl";'; 1527 $result = query($query, $connection); 1528 if (empty($result)) { 1529 $query = 'show variables like "have_openssl";'; 1530 $result = query($query, $connection); 1531 } 1532 $haveMySQLSSL = false; 1533 if (! empty($result)) { 1534 $ssl = $result[0]['Value']; 1535 $haveMySQLSSL = $ssl == 'YES'; 1536 } 1537 $s = ''; 1538 if ($haveMySQLSSL) { 1539 $query = 'show status like "Ssl_cipher";'; 1540 $result = query($query, $connection); 1541 $isSSL = ! empty($result[0]['Value']); 1542 } else { 1543 $isSSL = false; 1544 } 1545 if ($isSSL) { 1546 $msg = tra('MySQL SSL connection is active'); 1547 $s = tra('ON'); 1548 } elseif ($haveMySQLSSL && ! $isSSL) { 1549 $msg = tra('MySQL connection is not encrypted'); 1550 $s = tra('OFF'); 1551 } else { 1552 $msg = tra('MySQL Server does not have SSL activated.'); 1553 $s = 'OFF'; 1554 } 1555 $fitness = tra('info'); 1556 if ($s == tra('ON')) { 1557 $fitness = tra('good'); 1558 } 1559 $mysql_properties['SSL connection'] = array( 1560 'fitness' => $fitness, 1561 'setting' => $s, 1562 'message' => $msg 1563 ); 1564 1565 // Strict mode 1566 $query = 'SELECT @@sql_mode as Value;'; 1567 $result = query($query, $connection); 1568 $s = ''; 1569 $msg = 'Unable to query strict mode'; 1570 if (! empty($result)) { 1571 $sql_mode = $result[0]['Value']; 1572 $modes = explode(',', $sql_mode); 1573 1574 if (in_array('STRICT_ALL_TABLES', $modes)) { 1575 $s = 'STRICT_ALL_TABLES'; 1576 } 1577 if (in_array('STRICT_TRANS_TABLES', $modes)) { 1578 if (! empty($s)) { 1579 $s .= ','; 1580 } 1581 $s .= 'STRICT_TRANS_TABLES'; 1582 } 1583 1584 if (! empty($s)) { 1585 $msg = 'MySQL is using strict mode'; 1586 } else { 1587 $msg = 'MySQL is not using strict mode.'; 1588 } 1589 } 1590 $mysql_properties['Strict Mode'] = array( 1591 'fitness' => tra('info'), 1592 'setting' => $s, 1593 'message' => $msg 1594 ); 1595 1596 // MySQL Variables 1597 $query = "SHOW VARIABLES;"; 1598 $result = query($query, $connection); 1599 foreach ($result as $value) { 1600 $mysql_variables[$value['Variable_name']] = array('value' => $value['Value']); 1601 } 1602 1603 if (! $standalone) { 1604 $mysql_crashed_tables = array(); 1605 // This should give all crashed tables (MyISAM at least) - does need testing though !! 1606 $query = 'SHOW TABLE STATUS WHERE engine IS NULL AND comment <> "VIEW";'; 1607 $result = query($query, $connection); 1608 foreach ($result as $value) { 1609 $mysql_crashed_tables[$value['Name']] = array('Comment' => $value['Comment']); 1610 } 1611 } 1612} 1613 1614// Apache properties 1615 1616$apache_properties = false; 1617if (function_exists('apache_get_version')) { 1618 // Apache Modules 1619 $apache_modules = apache_get_modules(); 1620 1621 // mod_rewrite 1622 $s = false; 1623 $s = array_search('mod_rewrite', $apache_modules); 1624 if ($s) { 1625 $apache_properties['mod_rewrite'] = array( 1626 'setting' => 'Loaded', 1627 'fitness' => tra('good') , 1628 'message' => tra('Tiki needs this module for Search Engine Friendly URLs via .htaccess. However, it can\'t be checked if this web server respects configurations made in .htaccess. For further information go to Admin->SefURL in your Tiki.') 1629 ); 1630 } else { 1631 $apache_properties['mod_rewrite'] = array( 1632 'setting' => 'Not available', 1633 'fitness' => tra('unsure') , 1634 'message' => tra('Tiki needs this module for Search Engine Friendly URLs. For further information go to Admin->SefURL in the Tiki.') 1635 ); 1636 } 1637 1638 if (! $standalone) { 1639 // work out if RewriteBase is set up properly 1640 global $url_path; 1641 $enabledFileName = '.htaccess'; 1642 if (file_exists($enabledFileName)) { 1643 $enabledFile = fopen($enabledFileName, "r"); 1644 $rewritebase = '/'; 1645 while ($nextLine = fgets($enabledFile)) { 1646 if (preg_match('/^RewriteBase\s*(.*)$/', $nextLine, $m)) { 1647 $rewritebase = substr($m[1], -1) !== '/' ? $m[1] . '/' : $m[1]; 1648 break; 1649 } 1650 } 1651 if ($url_path == $rewritebase) { 1652 $smarty->assign('rewritebaseSetting', $rewritebase); 1653 $apache_properties['RewriteBase'] = array( 1654 'setting' => $rewritebase, 1655 'fitness' => tra('good') , 1656 'message' => tra('RewriteBase is set correctly in .htaccess. Search Engine Friendly URLs should work. Be aware, though, that this test can\'t checked if Apache really loads .htaccess.') 1657 ); 1658 } else { 1659 $apache_properties['RewriteBase'] = array( 1660 'setting' => $rewritebase, 1661 'fitness' => tra('bad') , 1662 'message' => tra('RewriteBase is not set correctly in .htaccess. Search Engine Friendly URLs are not going to work with this configuration. It should be set to "') . substr($url_path, 0, -1) . '".' 1663 ); 1664 } 1665 } else { 1666 $apache_properties['RewriteBase'] = array( 1667 'setting' => tra('Not found'), 1668 'fitness' => tra('info') , 1669 'message' => tra('The .htaccess file has not been activated, so this check cannot be performed. To use Search Engine Friendly URLs, activate .htaccess by copying _htaccess into its place (or a symlink if supported by your Operating System). Then do this check again.') 1670 ); 1671 } 1672 } 1673 1674 if ($pos = strpos($_SERVER['REQUEST_URI'], 'tiki-check.php')) { 1675 $sef_test_protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) ? 'https://' : 'http://'; 1676 $sef_test_base_url = $sef_test_protocol . $_SERVER['HTTP_HOST'] . substr($_SERVER['REQUEST_URI'], 0, $pos); 1677 $sef_test_ping_value = mt_rand(); 1678 $sef_test_url = $sef_test_base_url . 'tiki-check?tiki-check-ping=' . $sef_test_ping_value; 1679 $sef_test_folder_created = false; 1680 $sef_test_folder_writable = true; 1681 if ($standalone) { 1682 $sef_test_path_current = __DIR__; 1683 $sef_test_dir_name = 'tiki-check-' . $sef_test_ping_value; 1684 $sef_test_folder = $sef_test_path_current . DIRECTORY_SEPARATOR . $sef_test_dir_name; 1685 if (is_writable($sef_test_path_current)&&! file_exists($sef_test_folder)) { 1686 if (mkdir($sef_test_folder)) { 1687 $sef_test_folder_created = true; 1688 copy(__FILE__, $sef_test_folder . DIRECTORY_SEPARATOR . 'tiki-check.php'); 1689 file_put_contents($sef_test_folder . DIRECTORY_SEPARATOR . '.htaccess', "<IfModule mod_rewrite.c>\nRewriteEngine On\nRewriteRule tiki-check$ tiki-check.php [L]\n</IfModule>\n"); 1690 $sef_test_url = $sef_test_base_url . $sef_test_dir_name . '/tiki-check?tiki-check-ping=' . $sef_test_ping_value; 1691 } 1692 } else { 1693 $sef_test_folder_writable = false; 1694 } 1695 } 1696 1697 if (! $sef_test_folder_writable) { 1698 $apache_properties['SefURL Test'] = array( 1699 'setting' => tra('Not Working'), 1700 'fitness' => tra('info') , 1701 'message' => tra('The automated test could not run. The required files could not be created on the server to run the test. That may only mean that there were no permissions, but the Apache configuration should be checked. For further information go to Admin->SefURL in the Tiki.') 1702 ); 1703 } else { 1704 $pong_value = get_content_from_url($sef_test_url); 1705 if ($pong_value != 'fail-no-request-done') { 1706 if ('pong:' . $sef_test_ping_value == $pong_value) { 1707 $apache_properties['SefURL Test'] = array( 1708 'setting' => tra('Working'), 1709 'fitness' => tra('good') , 1710 'message' => tra('An automated test was done, and the server appears to be configured correctly to handle Search Engine Friendly URLs.') 1711 ); 1712 } else { 1713 if (strncmp('fail-http-', $pong_value, 10) == 0) { 1714 $apache_return_code = substr($pong_value, 10); 1715 $apache_properties['SefURL Test'] = array( 1716 'setting' => tra('Not Working'), 1717 'fitness' => tra('info') , 1718 'message' => sprintf(tra('An automated test was done and, based on the results, the server does not appear to be configured correctly to handle Search Engine Friendly URLs. The server returned an unexpected HTTP code: "%s". This automated test may fail due to the infrastructure setup, but the Apache configuration should be checked. For further information go to Admin->SefURL in your Tiki.'), $apache_return_code) 1719 ); 1720 } else { 1721 $apache_properties['SefURL Test'] = array( 1722 'setting' => tra('Not Working'), 1723 'fitness' => tra('info') , 1724 'message' => tra('An automated test was done and, based on the results, the server does not appear to be configured correctly to handle Search Engine Friendly URLs. This automated test may fail due to the infrastructure setup, but the Apache configuration should be checked. For further information go to Admin->SefURL in your Tiki.') 1725 ); 1726 } 1727 } 1728 } 1729 } 1730 if ($sef_test_folder_created) { 1731 unlink($sef_test_folder . DIRECTORY_SEPARATOR . 'tiki-check.php'); 1732 unlink($sef_test_folder . DIRECTORY_SEPARATOR . '.htaccess'); 1733 rmdir($sef_test_folder); 1734 } 1735 } 1736 1737 // mod_expires 1738 $s = false; 1739 $s = array_search('mod_expires', $apache_modules); 1740 if ($s) { 1741 $apache_properties['mod_expires'] = array( 1742 'setting' => 'Loaded', 1743 'fitness' => tra('good') , 1744 'message' => tra('With this module, the HTTP Expires header can be set, which increases performance. It can\'t be checked, though, if mod_expires is configured correctly.') 1745 ); 1746 } else { 1747 $apache_properties['mod_expires'] = array( 1748 'setting' => 'Not available', 1749 'fitness' => tra('unsure') , 1750 'message' => tra('With this module, the HTTP Expires header can be set, which increases performance. Once it is installed, it still needs to be configured correctly.') 1751 ); 1752 } 1753 1754 // mod_deflate 1755 $s = false; 1756 $s = array_search('mod_deflate', $apache_modules); 1757 if ($s) { 1758 $apache_properties['mod_deflate'] = array( 1759 'setting' => 'Loaded', 1760 'fitness' => tra('good') , 1761 'message' => tra('With this module, the data the webserver sends out can be compressed, which reduced data transfer amounts and increases performance. This test can\'t check, though, if mod_deflate is configured correctly.') 1762 ); 1763 } else { 1764 $apache_properties['mod_deflate'] = array( 1765 'setting' => 'Not available', 1766 'fitness' => tra('unsure') , 1767 'message' => tra('With this module, the data the webserver sends out can be compressed, which reduces data transfer amounts and increases performance. Once it is installed, it still needs to be configured correctly.') 1768 ); 1769 } 1770 1771 // mod_security 1772 $s = false; 1773 $s = array_search('mod_security', $apache_modules); 1774 if ($s) { 1775 $apache_properties['mod_security'] = array( 1776 'setting' => 'Loaded', 1777 'fitness' => tra('info') , 1778 'message' => tra('This module can increase security of Tiki and therefore the server, but be aware that it is very tricky to configure correctly. A misconfiguration can lead to failed page saves or other hard to trace bugs.') 1779 ); 1780 } else { 1781 $apache_properties['mod_security'] = array( 1782 'setting' => 'Not available', 1783 'fitness' => tra('info') , 1784 'message' => tra('This module can increase security of Tiki and therefore the server, but be aware that it is very tricky to configure correctly. A misconfiguration can lead to failed page saves or other hard to trace bugs.') 1785 ); 1786 } 1787 1788 // Get /server-info, if available 1789 if (function_exists('curl_init') && function_exists('curl_exec')) { 1790 $curl = curl_init(); 1791 curl_setopt($curl, CURLOPT_URL, 'http://localhost/server-info'); 1792 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 1793 curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); 1794 $apache_server_info = curl_exec($curl); 1795 if (curl_getinfo($curl, CURLINFO_HTTP_CODE) == 200) { 1796 $apache_server_info = preg_replace('%^.*<body>(.*)</body>.*$%ms', '$1', $apache_server_info); 1797 } else { 1798 $apache_server_info = false; 1799 } 1800 curl_close($curl); 1801 } else { 1802 $apache_server_info = 'nocurl'; 1803 } 1804} 1805 1806 1807// IIS Properties 1808$iis_properties = false; 1809 1810if (check_isIIS()) { 1811 // IIS Rewrite module 1812 if (check_hasIIS_UrlRewriteModule()) { 1813 $iis_properties['IIS Url Rewrite Module'] = array( 1814 'fitness' => tra('good'), 1815 'setting' => 'Available', 1816 'message' => tra('The URL Rewrite Module is required to use SEFURL on IIS.') 1817 ); 1818 } else { 1819 $iis_properties['IIS Url Rewrite Module'] = array( 1820 'fitness' => tra('bad'), 1821 'setting' => 'Not Available', 1822 'message' => tra('The URL Rewrite Module is required to use SEFURL on IIS.') 1823 ); 1824 } 1825} 1826 1827// Check Tiki Packages 1828if (! $standalone) { 1829 global $tikipath; 1830 1831 $composerManager = new ComposerManager($tikipath); 1832 $installedLibs = $composerManager->getInstalled(); 1833 1834 $packagesToCheck = array( 1835 array( 1836 'name' => 'media-alchemyst/media-alchemyst', 1837 'preferences' => array( 1838 'alchemy_ffmpeg_path' => array( 1839 'name' => tra('ffmpeg path'), 1840 'type' => 'path' 1841 ), 1842 'alchemy_ffprobe_path' => array( 1843 'name' => tra('ffprobe path'), 1844 'type' => 'path' 1845 ), 1846 'alchemy_unoconv_path' => array( 1847 'name' => tra('unoconv path'), 1848 'type' => 'path' 1849 ), 1850 'alchemy_gs_path' => array( 1851 'name' => tra('ghostscript path'), 1852 'type' => 'path' 1853 ), 1854 'alchemy_imagine_driver' => array( 1855 'name' => tra('Alchemy Image library'), 1856 'type' => 'classOptions', 1857 'options' => array( 1858 'imagick' => array( 1859 'name' => tra('Imagemagick'), 1860 'classLib' => 'Imagine\Imagick\Imagine', 1861 'className' => 'Imagick', 1862 'extension' => false 1863 ), 1864 'gd' => array( 1865 'name' => tra('GD'), 1866 'classLib' => 'Imagine\Gd\Imagine', 1867 'className' => false, 1868 'extension' => 'gd' 1869 ) 1870 ), 1871 ), 1872 ) 1873 ), 1874 array( 1875 'name' => 'php-unoconv/php-unoconv', 1876 'preferences' => array( 1877 'alchemy_unoconv_path' => array( 1878 'name' => tra('unoconv path'), 1879 'type' => 'path' 1880 ) 1881 ) 1882 ) 1883 ); 1884 1885 $packagesToDisplay = array(); 1886 foreach ($installedLibs as $installedPackage) { 1887 $key = array_search($installedPackage['name'], array_column($packagesToCheck, 'name')); 1888 if ($key !== false) { 1889 $warnings = checkPreferences($packagesToCheck[$key]['preferences']); 1890 checkPackageWarnings($warnings, $installedPackage); 1891 1892 $packageInfo = array( 1893 'name' => $installedPackage['name'], 1894 'version' => $installedPackage['installed'], 1895 'status' => count($warnings) > 0 ? tra('unsure') : tra('good'), 1896 'message' => $warnings 1897 ); 1898 } else { 1899 $packageInfo = array( 1900 'name' => $installedPackage['name'], 1901 'version' => $installedPackage['installed'], 1902 'status' => tra('good'), 1903 'message' => array() 1904 ); 1905 } 1906 $packagesToDisplay[] = $packageInfo; 1907 } 1908 1909 /** 1910 * Tesseract PHP Package Check 1911 */ 1912 1913 /** @var string The version of Tesseract required */ 1914 $TesseractVersion = '2.7.0'; 1915 /** @var string Current Tesseract installed version */ 1916 $ocrVersion = false; 1917 foreach ($packagesToDisplay as $arrayValue) { 1918 if ($arrayValue['name'] === 'thiagoalessio/tesseract_ocr') { 1919 $ocrVersion = $arrayValue['version']; 1920 break; 1921 } 1922 } 1923 1924 if (! $ocrVersion) { 1925 $ocrVersion = tra('Not Installed'); 1926 $ocrMessage = tra( 1927 'Tesseract PHP package could not be found. Try installing through Packages.' 1928 ); 1929 $ocrStatus = 'bad'; 1930 } elseif (version_compare($ocrVersion, $TesseractVersion, '>=')) { 1931 $ocrMessage = tra('Tesseract PHP dependency installed.'); 1932 $ocrStatus = 'good'; 1933 } else { 1934 $ocrMessage = tra( 1935 'The installed Tesseract version is lower than the required version.' 1936 ); 1937 $ocrStatus = 'bad'; 1938 } 1939 1940 $ocrToDisplay = array(array( 1941 'name' => tra('Tesseract package'), 1942 'version' => $ocrVersion, 1943 'status' => $ocrStatus, 1944 'message' => $ocrMessage, 1945 )); 1946 1947 /** 1948 * Tesseract Binary dependency Check 1949 */ 1950 1951 $ocr = Tikilib::lib('ocr'); 1952 $langCount = count($ocr->getTesseractLangs()); 1953 1954 if ($langCount >= 5) { 1955 $ocrMessage = $langCount . ' ' . tra('languages installed.'); 1956 $ocrStatus = 'good'; 1957 } else { 1958 $ocrMessage = tra( 1959 'Not all languages installed. You may need to install additional languages for multilingual support.' 1960 ); 1961 $ocrStatus = 'unsure'; 1962 } 1963 1964 $ocrToDisplay[] = array( 1965 'name' => tra('Tesseract languages'), 1966 'status' => $ocrStatus, 1967 'message' => $ocrMessage, 1968 ); 1969 1970 if ($ocrVersion !== 'Not Installed') { 1971 $ocrVersion = $ocr->getTesseractVersion(); 1972 } else { 1973 $ocrVersion = false; 1974 } 1975 1976 if (! $ocrVersion) { 1977 $ocrVersion = tra('Not Found'); 1978 $ocrMessage = tra( 1979 'Tesseract could not be found.' 1980 ); 1981 $ocrStatus = 'bad'; 1982 } elseif ($ocr->checkTesseractVersion()) { 1983 $ocrMessage = tra( 1984 'Tesseract meets or exceeds the version requirements.' 1985 ); 1986 $ocrStatus = 'good'; 1987 } else { 1988 $ocrMessage = tra( 1989 'The installed Tesseract version is lower than the required version.' 1990 ); 1991 $ocrStatus = 'bad'; 1992 } 1993 1994 $ocrToDisplay[] = array( 1995 'name' => tra('Tesseract binary'), 1996 'version' => $ocrVersion, 1997 'status' => $ocrStatus, 1998 'message' => $ocrMessage, 1999 ); 2000 try { 2001 if (empty($prefs['ocr_tesseract_path']) || $prefs['ocr_tesseract_path'] === 'tesseract') { 2002 $ocrStatus = 'bad'; 2003 $ocrMessage = tra( 2004 'Your path preference is not configured. It may work now but will likely fail with cron. Specify an absolute path.' 2005 ); 2006 } elseif ($prefs['ocr_tesseract_path'] === $ocr->whereIsExecutable('tesseract')) { 2007 $ocrStatus = 'good'; 2008 $ocrMessage = tra('Path setup correctly.'); 2009 } else { 2010 $ocrStatus = 'unsure'; 2011 $ocrMessage = tra( 2012 'Your path may not be configured correctly. It appears to be located at ' 2013 ) . $ocr->whereIsExecutable( 2014 'tesseract' . '.' 2015 ); 2016 } 2017 } catch (Exception $e) { 2018 if (empty($prefs['ocr_tesseract_path']) 2019 || $prefs['ocr_tesseract_path'] === 'tesseract' 2020 ) { 2021 $ocrStatus = 'bad'; 2022 $ocrMessage = tra( 2023 'Your path preference is not configured. It may work now but will likely fail with cron. Specify an absolute path.' 2024 ); 2025 } else { 2026 $ocrStatus = 'unsure'; 2027 $ocrMessage = tra( 2028 'Your path is configured, but we were unable to tell if it was configured properly or not.' 2029 ); 2030 } 2031 } 2032 2033 $ocrToDisplay[] = array( 2034 'name' => tra('Tesseract path'), 2035 'status' => $ocrStatus, 2036 'message' => $ocrMessage, 2037 ); 2038 2039 2040 $pdfimages = Tikilib::lib('pdfimages'); 2041 $pdfimages->setVersion(); 2042 2043 //lets fall back to configured options for a binary path if its not found with default options. 2044 if (! $pdfimages->version) { 2045 $pdfimages->setBinaryPath(); 2046 $pdfimages->setVersion(); 2047 } 2048 2049 if ($pdfimages->version) { 2050 $ocrStatus = 'good'; 2051 $ocrMessage = tra('It appears that pdfimages is installed on your system.'); 2052 } else { 2053 $ocrStatus = 'bad'; 2054 $ocrMessage = tra('Could not find pdfimages. PDF files will not be processed.'); 2055 } 2056 2057 $ocrToDisplay[] = array( 2058 'name' => tra('Pdfimages binary'), 2059 'version' => $pdfimages->version, 2060 'status' => $ocrStatus, 2061 'message' => $ocrMessage, 2062 ); 2063 2064 try { 2065 if (empty($prefs['ocr_pdfimages_path']) || $prefs['ocr_pdfimages_path'] === 'pdfimages') { 2066 $ocrStatus = 'bad'; 2067 $ocrMessage = tra('Your path preference is not configured. It may work now but will likely fail with cron. Specify an absolute path.'); 2068 } elseif ($prefs['ocr_pdfimages_path'] === $ocr->whereIsExecutable('pdfimages')) { 2069 $ocrStatus = 'good'; 2070 $ocrMessage = tra('Path setup correctly'); 2071 } else { 2072 $ocrStatus = 'unsure'; 2073 $ocrMessage = tra('Your path may not be configured correctly. It appears to be located at ') . 2074 $ocr->whereIsExecutable('pdfimages' . ' '); 2075 } 2076 } catch (Exception $e) { 2077 if (empty($prefs['ocr_pdfimages_path']) || $prefs['ocr_pdfimages_path'] === 'pdfimages') { 2078 $ocrStatus = 'bad'; 2079 $ocrMessage = tra('Your path preference is not configured. It may work now but will likely fail with cron. Specify an absolute path.'); 2080 } else { 2081 $ocrStatus = 'unsure'; 2082 $ocrMessage = tra( 2083 'Your path is configured, but we were unable to tell if it was configured properly or not.' 2084 ); 2085 } 2086 } 2087 2088 $ocrToDisplay[] = array( 2089 'name' => tra('Pdfimages path'), 2090 'status' => $ocrStatus, 2091 'message' => $ocrMessage, 2092 ); 2093 2094 // check if scheduler is set up properly. 2095 $scheduleDb = $ocr->table('tiki_scheduler'); 2096 $conditions['status'] = 'active'; 2097 $conditions['params'] = $scheduleDb->contains('ocr:all'); 2098 if ($scheduleDb->fetchBool($conditions)) { 2099 $ocrToDisplay[] = array( 2100 'name' => tra('Scheduler'), 2101 'status' => 'good', 2102 'message' => tra('Scheduler has been successfully setup.'), 2103 ); 2104 } else { 2105 $ocrToDisplay[] = array( 2106 'name' => tra('Scheduler'), 2107 'status' => 'bad', 2108 'message' => tra('Scheduler needs to have a console command of "ocr:all" set.'), 2109 ); 2110 } 2111 2112 $smarty->assign('ocr', $ocrToDisplay); 2113} 2114// Security Checks 2115// get all dangerous php settings and check them 2116$security = false; 2117 2118// check file upload dir and compare it to tiki root dir 2119$s = ini_get('upload_tmp_dir'); 2120$sn = substr($_SERVER['SCRIPT_NAME'], 0, -14); 2121if ($s != "" && strpos($sn, $s) !== false) { 2122 $security['upload_tmp_dir'] = array( 2123 'fitness' => tra('unsafe') , 2124 'setting' => $s, 2125 'message' => tra('upload_tmp_dir is probably inside the Tiki directory. There is a risk that someone can upload any file to this directory and access it via web browser.') 2126 ); 2127} else { 2128 $security['upload_tmp_dir'] = array( 2129 'fitness' => tra('unknown') , 2130 'setting' => $s, 2131 'message' => tra('It can\'t be reliably determined if the upload_tmp_dir is accessible via a web browser. To be sure, check the webserver configuration.') 2132 ); 2133} 2134 2135// Determine system state 2136$pdf_webkit = ''; 2137if (isset($prefs) && $prefs['print_pdf_from_url'] == 'webkit') { 2138 $pdf_webkit = '<b>' . tra('WebKit is enabled') . '.</b> '; 2139} 2140$feature_blogs = ''; 2141if (isset($prefs) && $prefs['feature_blogs'] == 'y') { 2142 $feature_blogs = '<b>' . tra('The Blogs feature is enabled') . '.</b> '; 2143} 2144 2145$fcts = array( 2146 array( 2147 'function' => 'exec', 2148 'risky' => tra('Exec can potentially be used to execute arbitrary code on the server.') . ' ' . tra('Tiki does not need it; perhaps it should be disabled.') . ' ' . tra('However, the Plugins R/RR need it. If you use the Plugins R/RR and the other PHP software on the server can be trusted, this should be enabled.'), 2149 'safe' => tra('Exec can be potentially be used to execute arbitrary code on the server.') . ' ' . tra('Tiki needs it to run the Plugins R/RR.') . tra('If this is needed and the other PHP software on the server can be trusted, this should be enabled.') 2150 ), 2151 array( 2152 'function' => 'passthru', 2153 'risky' => tra('Passthru is similar to exec.') . ' ' . tra('Tiki does not need it; perhaps it should be disabled. However, the Composer package manager used for installations in Subversion checkouts may need it.'), 2154 'safe' => tra('Passthru is similar to exec.') . ' ' . tra('Tiki does not need it; it is good that it is disabled. However, the Composer package manager used for installations in Subversion checkouts may need it.') 2155 ), 2156 array( 2157 'function' => 'shell_exec', 2158 'risky' => tra('Shell_exec is similar to exec.') . ' ' . tra('Tiki needs it to run PDF from URL: WebKit (wkhtmltopdf). ' . $pdf_webkit . 'If this is needed and the other PHP software on the server can be trusted, this should be enabled.'), 2159 'safe' => tra('Shell_exec is similar to exec.') . ' ' . tra('Tiki needs it to run PDF from URL: WebKit (wkhtmltopdf). ' . $pdf_webkit . 'If this is needed and the other PHP software on the server can be trusted, this should be enabled.') 2160 ), 2161 array( 2162 'function' => 'system', 2163 'risky' => tra('System is similar to exec.') . ' ' . tra('Tiki does not need it; perhaps it should be disabled.'), 2164 'safe' => tra('System is similar to exec.') . ' ' . tra('Tiki does not need it; it is good that it is disabled.') 2165 ), 2166 array( 2167 'function' => 'proc_open', 2168 'risky' => tra('Proc_open is similar to exec.') . ' ' . tra('Tiki does not need it; perhaps it should be disabled. However, the Composer package manager used for installations in Subversion checkouts or when using the package manager from the <a href="https://doc.tiki.org/Packages" target="_blank">admin interface</a> may need it.'), 2169 'safe' => tra('Proc_open is similar to exec.') . ' ' . tra('Tiki does not need it; it is good that it is disabled. However, the Composer package manager used for installations in Subversion checkouts or when using the package manager from the <a href="https://doc.tiki.org/Packages" target="_blank">admin interface</a> may need it.') 2170 ), 2171 array( 2172 'function' => 'popen', 2173 'risky' => tra('popen is similar to exec.') . ' ' . tra('Tiki needs it for file search indexing in file galleries. If this is needed and other PHP software on the server can be trusted, this should be enabled.'), 2174 'safe' => tra('popen is similar to exec.') . ' ' . tra('Tiki needs it for file search indexing in file galleries. If this is needed and other PHP software on the server can be trusted, this should be enabled.') 2175 ), 2176 array( 2177 'function' => 'curl_exec', 2178 'risky' => tra('Curl_exec can potentially be abused to write malicious code.') . ' ' . tra('Tiki needs it to run features like Kaltura, CAS login, CClite and the myspace and sf wiki-plugins. If these are needed and other PHP software on the server can be trusted, this should be enabled.'), 2179 'safe' => tra('Curl_exec can potentially be abused to write malicious code.') . ' ' . tra('Tiki needs it to run features like Kaltura, CAS login, CClite and the myspace and sf wiki-plugins. If these are needed and other PHP software on the server can be trusted, this should be enabled.') 2180 ), 2181 array( 2182 'function' => 'curl_multi_exec', 2183 'risky' => tra('Curl_multi_exec can potentially be abused to write malicious code.') . ' ' . tra('Tiki needs it to run features like Kaltura, CAS login, CClite and the myspace and sf wiki-plugins. If these are needed and other PHP software on the server can be trusted, this should be enabled.'), 2184 'safe' => tra('Curl_multi_exec can potentially be abused to write malicious code.') . ' ' . tra('Tiki needs it to run features like Kaltura, CAS login, CClite and the myspace and sf wiki-plugins. If these are needed and other PHP software on the server can be trusted, this should be enabled.') 2185 ), 2186 array( 2187 'function' => 'parse_ini_file', 2188 'risky' => tra('It is probably an urban myth that this is dangerous. Tiki team will reconsider this check, but be warned.') . ' ' . tra('It is required for the <a href="http://doc.tiki.org/System+Configuration" target="_blank">System Configuration</a> feature.'), 2189 'safe' => tra('It is probably an urban myth that this is dangerous. Tiki team will reconsider this check, but be warned.') . ' ' . tra('It is required for the <a href="http://doc.tiki.org/System+Configuration" target="_blank">System Configuration</a> feature.'), 2190 ), 2191 array( 2192 'function' => 'show_source', 2193 'risky' => tra('It is probably an urban myth that this is dangerous. Tiki team will reconsider this check, but be warned.'), 2194 'safe' => tra('It is probably an urban myth that this is dangerous. Tiki team will reconsider this check, but be warned.'), 2195 ) 2196 ); 2197 2198foreach ($fcts as $fct) { 2199 if (function_exists($fct['function'])) { 2200 $security[$fct['function']] = array( 2201 'setting' => tra('Enabled'), 2202 'fitness' => tra('risky'), 2203 'message' => $fct['risky'] 2204 ); 2205 } else { 2206 $security[$fct['function']] = array( 2207 'setting' => tra('Disabled'), 2208 'fitness' => tra('safe'), 2209 'message' => $fct['safe'] 2210 ); 2211 } 2212} 2213 2214// trans_sid 2215$s = ini_get('session.use_trans_sid'); 2216if ($s) { 2217 $security['session.use_trans_sid'] = array( 2218 'setting' => 'Enabled', 2219 'fitness' => tra('unsafe'), 2220 'message' => tra('session.use_trans_sid should be off by default. See the PHP manual for details.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 2221 ); 2222} else { 2223 $security['session.use_trans_sid'] = array( 2224 'setting' => 'Disabled', 2225 'fitness' => tra('safe'), 2226 'message' => tra('session.use_trans_sid should be off by default. See the PHP manual for details.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 2227 ); 2228} 2229 2230$s = ini_get('xbithack'); 2231if ($s == 1) { 2232 $security['xbithack'] = array( 2233 'setting' => 'Enabled', 2234 'fitness' => tra('unsafe'), 2235 'message' => tra('Setting the xbithack option is unsafe. Depending on the file handling of the webserver and the Tiki settings, an attacker may be able to upload scripts to file gallery and execute them.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 2236 ); 2237} else { 2238 $security['xbithack'] = array( 2239 'setting' => 'Disabled', 2240 'fitness' => tra('safe'), 2241 'message' => tra('setting the xbithack option is unsafe. Depending on the file handling of the webserver and the Tiki settings, an attacker may be able to upload scripts to file gallery and execute them.') . ' <a href="#php_conf_info">' . tra('How to change this value') . '</a>' 2242 ); 2243} 2244 2245$s = ini_get('allow_url_fopen'); 2246if ($s == 1) { 2247 $security['allow_url_fopen'] = array( 2248 'setting' => 'Enabled', 2249 'fitness' => tra('risky'), 2250 'message' => tra('allow_url_fopen may potentially be used to upload remote data or scripts. Also used by Composer to fetch dependencies. ' . $feature_blogs . 'If this Tiki does not use the Blogs feature, this can be switched off.') 2251 ); 2252} else { 2253 $security['allow_url_fopen'] = array( 2254 'setting' => 'Disabled', 2255 'fitness' => tra('safe'), 2256 'message' => tra('allow_url_fopen may potentially be used to upload remote data or scripts. Also used by Composer to fetch dependencies. ' . $feature_blogs . 'If this Tiki does not use the Blogs feature, this can be switched off.') 2257 ); 2258} 2259 2260if ($standalone || (! empty($prefs) && $prefs['fgal_enable_auto_indexing'] === 'y')) { 2261 // adapted from \FileGalLib::get_file_handlers 2262 $fh_possibilities = array( 2263 'application/ms-excel' => array('xls2csv %1'), 2264 'application/msexcel' => array('xls2csv %1'), 2265 // vnd.openxmlformats are handled natively in Zend 2266 //'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => array('xlsx2csv.py %1'), 2267 'application/ms-powerpoint' => array('catppt %1'), 2268 'application/mspowerpoint' => array('catppt %1'), 2269 //'application/vnd.openxmlformats-officedocument.presentationml.presentation' => array('pptx2txt.pl %1 -'), 2270 'application/msword' => array('catdoc %1', 'strings %1'), 2271 //'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => array('docx2txt.pl %1 -'), 2272 'application/pdf' => array('pstotext %1', 'pdftotext %1 -'), 2273 'application/postscript' => array('pstotext %1'), 2274 'application/ps' => array('pstotext %1'), 2275 'application/rtf' => array('catdoc %1'), 2276 'application/sgml' => array('col -b %1', 'strings %1'), 2277 'application/vnd.ms-excel' => array('xls2csv %1'), 2278 'application/vnd.ms-powerpoint' => array('catppt %1'), 2279 'application/x-msexcel' => array('xls2csv %1'), 2280 'application/x-pdf' => array('pstotext %1', 'pdftotext %1 -'), 2281 'application/x-troff-man' => array('man -l %1'), 2282 'application/zip' => array('unzip -l %1'), 2283 'text/enriched' => array('col -b %1', 'strings %1'), 2284 'text/html' => array('elinks -dump -no-home %1'), 2285 'text/richtext' => array('col -b %1', 'strings %1'), 2286 'text/sgml' => array('col -b %1', 'strings %1'), 2287 'text/tab-separated-values' => array('col -b %1', 'strings %1'), 2288 ); 2289 2290 $fh_native = array( 2291 'application/pdf' => 18.0, 2292 'application/x-pdf' => 18.0, 2293 ); 2294 2295 $file_handlers = array(); 2296 if (! $standalone) { 2297 $tikiWikiVersion = new TWVersion(); 2298 } 2299 2300 foreach ($fh_possibilities as $type => $options) { 2301 $file_handler = array( 2302 'fitness' => '', 2303 'message' => '', 2304 ); 2305 2306 if (! $standalone && array_key_exists($type, $fh_native)) { 2307 if ($tikiWikiVersion->getBaseVersion() >= $fh_native["$type"]) { 2308 $file_handler['fitness'] = 'good'; 2309 $file_handler['message'] = "will be handled natively"; 2310 } 2311 } 2312 if ($standalone && array_key_exists($type, $fh_native)) { 2313 $file_handler['fitness'] = 'info'; 2314 $file_handler['message'] = "will be handled natively by Tiki >= " . $fh_native["$type"]; 2315 } 2316 if ($file_handler['fitness'] == '' || $file_handler['fitness'] == 'info') { 2317 foreach ($options as $opt) { 2318 $optArray = explode(' ', $opt, 2); 2319 $exec = reset($optArray); 2320 $which_exec = `which $exec`; 2321 if ($which_exec) { 2322 if ($file_handler['fitness'] == 'info') { 2323 $file_handler['message'] .= ", otherwise handled by $which_exec"; 2324 } else { 2325 $file_handler['message'] = "will be handled by $which_exec"; 2326 } 2327 $file_handler['fitness'] = 'good'; 2328 break; 2329 } 2330 } 2331 if ($file_handler['fitness'] == 'info') { 2332 $fh_commands = ''; 2333 foreach ($options as $opt) { 2334 $fh_commands .= $fh_commands ? ' or ' : ''; 2335 $fh_commands .= '"' . substr($opt, 0, strpos($opt, ' ')) . '"'; 2336 } 2337 $file_handler['message'] .= ', otherwise you need to install ' . $fh_commands . ' to index this type of file'; 2338 } 2339 } 2340 if (! $file_handler['fitness']) { 2341 $file_handler['fitness'] = 'unsure'; 2342 $fh_commands = ''; 2343 foreach ($options as $opt) { 2344 $fh_commands .= $fh_commands ? ' or ' : ''; 2345 $fh_commands .= '"' . substr($opt, 0, strpos($opt, ' ')) . '"'; 2346 } 2347 $file_handler['message'] = 'You need to install ' . $fh_commands . ' to index this type of file'; 2348 } 2349 $file_handlers[$type] = $file_handler; 2350 } 2351} 2352 2353 2354if (! $standalone) { 2355 // The following is borrowed from tiki-admin_system.php 2356 if ($prefs['feature_forums'] == 'y') { 2357 $dirs = TikiLib::lib('comments')->list_directories_to_save(); 2358 } else { 2359 $dirs = array(); 2360 } 2361 if ($prefs['feature_galleries'] == 'y' && ! empty($prefs['gal_use_dir'])) { 2362 $dirs[] = $prefs['gal_use_dir']; 2363 } 2364 if ($prefs['feature_file_galleries'] == 'y' && ! empty($prefs['fgal_use_dir'])) { 2365 $dirs[] = $prefs['fgal_use_dir']; 2366 } 2367 if ($prefs['feature_trackers'] == 'y') { 2368 if (! empty($prefs['t_use_dir'])) { 2369 $dirs[] = $prefs['t_use_dir']; 2370 } 2371 $dirs[] = 'img/trackers'; 2372 } 2373 if ($prefs['feature_wiki'] == 'y') { 2374 if (! empty($prefs['w_use_dir'])) { 2375 $dirs[] = $prefs['w_use_dir']; 2376 } 2377 if ($prefs['feature_create_webhelp'] == 'y') { 2378 $dirs[] = 'whelp'; 2379 } 2380 $dirs[] = 'img/wiki'; 2381 $dirs[] = 'img/wiki_up'; 2382 } 2383 $dirs = array_unique($dirs); 2384 $dirsExist = array(); 2385 foreach ($dirs as $i => $d) { 2386 $dirsWritable[$i] = is_writable($d); 2387 } 2388 $smarty->assign_by_ref('dirs', $dirs); 2389 $smarty->assign_by_ref('dirsWritable', $dirsWritable); 2390 2391 // Prepare Monitoring acks 2392 $query = "SELECT `value` FROM tiki_preferences WHERE `name`='tiki_check_status'"; 2393 $result = $tikilib->getOne($query); 2394 $last_state = json_decode($result, true); 2395 $smarty->assign_by_ref('last_state', $last_state); 2396 2397 function deack_on_state_change(&$check_group, $check_group_name) 2398 { 2399 global $last_state; 2400 foreach ($check_group as $key => $value) { 2401 if (! empty($last_state["$check_group_name"]["$key"])) { 2402 $check_group["$key"]['ack'] = $last_state["$check_group_name"]["$key"]['ack']; 2403 if (isset($check_group["$key"]['setting']) && isset($last_state["$check_group_name"]["$key"]['setting']) && 2404 $check_group["$key"]['setting'] != $last_state["$check_group_name"]["$key"]['setting']) { 2405 $check_group["$key"]['ack'] = false; 2406 } 2407 } 2408 } 2409 } 2410 deack_on_state_change($mysql_properties, 'MySQL'); 2411 deack_on_state_change($server_properties, 'Server'); 2412 if ($apache_properties) { 2413 deack_on_state_change($apache_properties, 'Apache'); 2414 } 2415 if ($iis_properties) { 2416 deack_on_state_change($iis_properties, 'IIS'); 2417 } 2418 deack_on_state_change($php_properties, 'PHP'); 2419 deack_on_state_change($security, 'PHP Security'); 2420 2421 $tikiWikiVersion = new TWVersion(); 2422 if (version_compare($tikiWikiVersion->getBaseVersion(), '18.0', '<') && ! class_exists('mPDF') 2423 || version_compare($tikiWikiVersion->getBaseVersion(), '18.0', '>=') && ! class_exists('\\Mpdf\\Mpdf')) { 2424 $smarty->assign('mPDFClassMissing', true); 2425 } 2426 2427 // Engine tables type 2428 global $dbs_tiki; 2429 if (! empty($dbs_tiki)) { 2430 $engineType = ''; 2431 $query = 'SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_NAME = "tiki_schema" AND TABLE_SCHEMA = "' . $dbs_tiki . '";'; 2432 $result = query($query, $connection); 2433 if (! empty($result[0]['ENGINE'])) { 2434 $engineType = $result[0]['ENGINE']; 2435 } 2436 } 2437 if (version_compare($tikiWikiVersion->getBaseVersion(), '18.0', '>=') && ! empty($dbs_tiki) && $engineType != 'InnoDB') { 2438 $smarty->assign('engineTypeNote', true); 2439 } else { 2440 $smarty->assign('engineTypeNote', false); 2441 } 2442 2443 $smarty->assign('composer_available', $composerManager->composerIsAvailable()); 2444 $smarty->assign('packages', $packagesToDisplay); 2445} 2446 2447$sensitiveDataDetectedFiles = array(); 2448check_for_remote_readable_files($sensitiveDataDetectedFiles); 2449 2450if (! empty($sensitiveDataDetectedFiles)) { 2451 $files = ' (Files: ' . trim(implode(', ', $sensitiveDataDetectedFiles)) . ')'; 2452 $tiki_security['Sensitive Data Exposure'] = array( 2453 'fitness' => tra('risky'), 2454 'message' => tra('Tiki detected that there are temporary files in the db folder that may expose credentials or other sensitive information.') . $files 2455 ); 2456} else { 2457 $tiki_security['Sensitive Data Exposure'] = array( 2458 'fitness' => tra('safe'), 2459 'message' => tra('Tiki did not detect temporary files in the db folder that may expose credentials or other sensitive information.') 2460 ); 2461} 2462 2463if (isset($_REQUEST['benchmark'])) { 2464 $benchmark = BenchmarkPhp::run(); 2465} else { 2466 $benchmark = ''; 2467} 2468 2469/** 2470 * TRIM (Tiki Remote Instance Manager) Section 2471 **/ 2472if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 2473 $trimCapable = false; 2474} else { 2475 $trimCapable = true; 2476} 2477 2478if ($trimCapable) { 2479 $trimServerRequirements = array(); 2480 $trimClientRequirements = array(); 2481 2482 $trimServerRequirements['Operating System Path'] = array( 2483 'fitness' => tra('info'), 2484 'message' => $_SERVER['PATH'] 2485 ); 2486 2487 $trimClientRequirements['Operating System Path'] = array( 2488 'fitness' => tra('info'), 2489 'message' => $_SERVER['PATH'] 2490 ); 2491 2492 $trimClientRequirements['SSH or FTP server'] = array( 2493 'fitness' => tra('info'), 2494 'message' => tra('To manage this instance from a remote server you need SSH or FTP access to this server') 2495 ); 2496 2497 $serverCommands = array( 2498 'make' => 'make', 2499 'php-cli' => 'php', 2500 'rsync' => 'rsync', 2501 'nice' => 'nice', 2502 'tar' => 'tar', 2503 'bzip2' => 'bzip2', 2504 'ssh' => 'ssh', 2505 'ssh-copy-id' => 'ssh-copy-id', 2506 'scp' => 'scp', 2507 'sqlite' => 'sqlite3' 2508 ); 2509 2510 $serverPHPExtensions = array( 2511 'php-sqlite' => 'sqlite3', 2512 ); 2513 2514 $clientCommands = array( 2515 'php-cli' => 'php', 2516 'mysql' => 'mysql', 2517 'mysqldump' => 'mysqldump', 2518 'gzip' => 'gzip', 2519 ); 2520 2521 foreach ($serverCommands as $key => $command) { 2522 if (commandIsAvailable($command)) { 2523 $trimServerRequirements[$key] = array( 2524 'fitness' => tra('good'), 2525 'message' => tra('Command found') 2526 ); 2527 } else { 2528 $trimServerRequirements[$key] = array( 2529 'fitness' => tra('unsure'), 2530 'message' => tra('Command not found, check if it is installed and available in one of the paths above') 2531 ); 2532 } 2533 } 2534 2535 foreach ($serverPHPExtensions as $key => $extension) { 2536 if (extension_loaded($extension)) { 2537 $trimServerRequirements[$key] = array( 2538 'fitness' => tra('good'), 2539 'message' => tra('Extension loaded in PHP') 2540 ); 2541 } else { 2542 $trimServerRequirements[$key] = array( 2543 'fitness' => tra('unsure'), 2544 'message' => tra('Extension not loaded in PHP') 2545 ); 2546 } 2547 } 2548 2549 foreach ($clientCommands as $key => $command) { 2550 if (commandIsAvailable($command)) { 2551 $trimClientRequirements[$key] = array( 2552 'fitness' => tra('good'), 2553 'message' => tra('Command found') 2554 ); 2555 } else { 2556 $trimClientRequirements[$key] = array( 2557 'fitness' => tra('unsure'), 2558 'message' => tra('Command not found, check if it is installed and available in one of the paths above') 2559 ); 2560 } 2561 } 2562} 2563 2564if ($standalone && ! $nagios) { 2565 $render .= '<style type="text/css">td, th { border: 1px solid #000000; vertical-align: baseline; padding: .5em; }</style>'; 2566// $render .= '<h1>Tiki Server Compatibility</h1>'; 2567 if (! $locked) { 2568 $render .= '<h2>MySQL or MariaDB Database Properties</h2>'; 2569 renderTable($mysql_properties); 2570 $render .= '<h2>Test sending emails</h2>'; 2571 if (isset($_REQUEST['email_test_to'])) { 2572 $email = filter_var($_POST['email_test_to'], FILTER_SANITIZE_EMAIL); 2573 $email_test_headers = 'From: noreply@tiki.org' . "\n"; // needs a valid sender 2574 $email_test_headers .= 'Reply-to: ' . $email . "\n"; 2575 $email_test_headers .= "Content-type: text/plain; charset=utf-8\n"; 2576 $email_test_headers .= 'X-Mailer: Tiki-Check - PHP/' . phpversion() . "\n"; 2577 $email_test_subject = tra('Test mail from Tiki Server Compatibility Test'); 2578 $email_test_body = tra("Congratulations!\n\nThis server can send emails.\n\n"); 2579 $email_test_body .= "\t" . tra('Server:') . ' ' . (empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['SERVER_NAME']) . "\n"; 2580 $email_test_body .= "\t" . tra('Sent:') . ' ' . date(DATE_RFC822) . "\n"; 2581 2582 $sentmail = mail($email, $email_test_subject, $email_test_body, $email_test_headers); 2583 if ($sentmail) { 2584 $mail['Sending mail'] = array( 2585 'setting' => 'Accepted', 2586 'fitness' => tra('good'), 2587 'message' => tra('It was possible to send an e-mail. This only means that a mail server accepted the mail for delivery. This check can\;t verify if that server actually delivered the mail. Please check the inbox of ' . htmlspecialchars($email) . ' to see if the mail was delivered.') 2588 ); 2589 } else { 2590 $mail['Sending mail'] = array( 2591 'setting' => 'Not accepted', 2592 'fitness' => tra('bad'), 2593 'message' => tra('It was not possible to send an e-mail. It may be that there is no mail server installed on this machine or that it is incorrectly configured. If the local mail server cannot be made to work, a regular mail account can be set up and its SMTP settings configured in tiki-admin.php.') 2594 ); 2595 } 2596 renderTable($mail); 2597 } else { 2598 $render .= '<form method="post" action="' . $_SERVER['SCRIPT_NAME'] . '">'; 2599 $render .= '<p><label for="e-mail">e-mail address to send test mail to</label>: <input type="text" id="email_test_to" name="email_test_to" /></p>'; 2600 $render .= '<p><input type="submit" class="btn btn-primary btn-sm" value=" Send e-mail " /></p>'; 2601 $render .= '<p><input type="hidden" id="dbhost" name="dbhost" value="'; 2602 if (isset($_POST['dbhost'])) { 2603 $render .= htmlentities(strip_tags($_POST['dbhost'])); 2604 }; 2605 $render .= '" /></p>'; 2606 $render .= '<p><input type="hidden" id="dbuser" name="dbuser" value="'; 2607 if (isset($_POST['dbuser'])) { 2608 $render .= htmlentities(strip_tags($_POST['dbuser'])); 2609 }; 2610 $render .= '"/></p>'; 2611 $render .= '<p><input type="hidden" id="dbpass" name="dbpass" value="'; 2612 if (isset($_POST['dbpass'])) { 2613 $render .= htmlentities(strip_tags($_POST['dbpass'])); 2614 }; 2615 $render .= '"/></p>'; 2616 $render .= '</form>'; 2617 } 2618 } 2619 2620 $render .= '<h2>Server Information</h2>'; 2621 renderTable($server_information); 2622 $render .= '<h2>Server Properties</h2>'; 2623 renderTable($server_properties); 2624 $render .= '<h2>Apache properties</h2>'; 2625 if ($apache_properties) { 2626 renderTable($apache_properties); 2627 if ($apache_server_info != 'nocurl' && $apache_server_info != false) { 2628 if (isset($_REQUEST['apacheinfo']) && $_REQUEST['apacheinfo'] == 'y') { 2629 $render .= $apache_server_info; 2630 } else { 2631 $render .= '<a href="' . $_SERVER['SCRIPT_NAME'] . '?apacheinfo=y">Append Apache /server-info;</a>'; 2632 } 2633 } elseif ($apache_server_info == 'nocurl') { 2634 $render .= 'You don\'t have the Curl extension in PHP, so we can\'t append Apache\'s server-info.'; 2635 } else { 2636 $render .= 'Apparently you have not enabled mod_info in your Apache, so we can\'t append more verbose information to this output.'; 2637 } 2638 } else { 2639 $render .= 'You are either not running the preferred Apache web server or you are running PHP with a SAPI that does not allow checking Apache properties (for example, CGI or FPM).'; 2640 } 2641 $render .= '<h2>IIS properties</h2>'; 2642 if ($iis_properties) { 2643 renderTable($iis_properties); 2644 } else { 2645 $render .= "You are not running IIS web server."; 2646 } 2647 $render .= '<h2>PHP scripting language properties</h2>'; 2648 renderTable($php_properties); 2649 2650 $render_sapi_info = ''; 2651 if (! empty($php_sapi_info)) { 2652 if (! empty($php_sapi_info['message'])) { 2653 $render_sapi_info .= $php_sapi_info['message']; 2654 } 2655 if (! empty($php_sapi_info['link'])) { 2656 $render_sapi_info .= '<a href="' . $php_sapi_info['link'] . '"> ' . $php_sapi_info['link'] . '</a>'; 2657 } 2658 $render_sapi_info = '<p>' . $render_sapi_info . '</p>'; 2659 } 2660 2661 $render .= '<p><a name="php_conf_info"></a>Change PHP configuration values:' . $render_sapi_info . ' You can check the full documentation on how to change the configurations values in <a href="http://www.php.net/manual/en/configuration.php">http://www.php.net/manual/en/configuration.php</a></p>'; 2662 $render .= '<h2>PHP security properties</h2>'; 2663 renderTable($security); 2664 $render .= '<h2>Tiki Security</h2>'; 2665 renderTable($tiki_security); 2666 $render .= '<h2>MySQL Variables</h2>'; 2667 renderTable($mysql_variables, 'wrap'); 2668 2669 $render .= '<h2>File Gallery Search Indexing</h2>'; 2670 $render .= '<em>More info <a href="https://doc.tiki.org/Search+within+files">here</a></em> 2671 '; 2672 renderTable($file_handlers); 2673 2674 $render .= '<h2>PHP Info</h2>'; 2675 if (isset($_REQUEST['phpinfo']) && $_REQUEST['phpinfo'] == 'y') { 2676 ob_start(); 2677 phpinfo(); 2678 $info = ob_get_contents(); 2679 ob_end_clean(); 2680 $info = preg_replace('%^.*<body>(.*)</body>.*$%ms', '$1', $info); 2681 $render .= $info; 2682 } else { 2683 $render .= '<a href="' . $_SERVER['SCRIPT_NAME'] . '?phpinfo=y">Append phpinfo();</a>'; 2684 } 2685 2686 $render .= '<a name="benchmark"></a><h2>Benchmark PHP/MySQL</h2>'; 2687 $render .= '<a href="tiki-check.php?benchmark=run&ts=' . time() . '#benchmark" style="margin-bottom: 10px;">Check</a>'; 2688 if (! empty($benchmark)) { 2689 renderTable($benchmark); 2690 } 2691 2692 $render .= '<h2>TRIM</h2>'; 2693 $render .= '<em>For more detailed information about Tiki Remote Instance Manager please check <a href="https://doc.tiki.org/TRIM">doc.tiki.org</a></em>.'; 2694 if ($trimCapable) { 2695 $render .= '<h3>Server Instance</h3>'; 2696 renderTable($trimServerRequirements); 2697 $render .= '<h3>Client Instance</h3>'; 2698 renderTable($trimClientRequirements); 2699 } else { 2700 $render .= '<p>Apparently Tiki is running on a Windows based server. This feature is not supported natively.</p>'; 2701 } 2702 2703 createPage('Tiki Server Compatibility', $render); 2704} elseif ($nagios) { 2705// 0 OK 2706// 1 WARNING 2707// 2 CRITICAL 2708// 3 UNKNOWN 2709 $monitoring_info = array( 'state' => 0, 2710 'message' => ''); 2711 2712 function update_overall_status($check_group, $check_group_name) 2713 { 2714 global $monitoring_info; 2715 $state = 0; 2716 $message = ''; 2717 2718 foreach ($check_group as $property => $values) { 2719 if (! isset($values['ack']) || $values['ack'] != true) { 2720 switch ($values['fitness']) { 2721 case 'unsure': 2722 $state = max($state, 1); 2723 $message .= "$property" . "->unsure, "; 2724 break; 2725 case 'risky': 2726 $state = max($state, 1); 2727 $message .= "$property" . "->risky, "; 2728 break; 2729 case 'bad': 2730 $state = max($state, 2); 2731 $message .= "$property" . "->BAD, "; 2732 break; 2733 case 'info': 2734 $state = max($state, 3); 2735 $message .= "$property" . "->info, "; 2736 break; 2737 case 'good': 2738 case 'safe': 2739 break; 2740 } 2741 } 2742 } 2743 $monitoring_info['state'] = max($monitoring_info['state'], $state); 2744 if ($state != 0) { 2745 $monitoring_info['message'] .= $check_group_name . ": " . trim($message, ' ,') . " -- "; 2746 } 2747 } 2748 2749 // Might not be set, i.e. in standalone mode 2750 if ($mysql_properties) { 2751 update_overall_status($mysql_properties, "MySQL"); 2752 } 2753 update_overall_status($server_properties, "Server"); 2754 if ($apache_properties) { 2755 update_overall_status($apache_properties, "Apache"); 2756 } 2757 if ($iis_properties) { 2758 update_overall_status($iis_properties, "IIS"); 2759 } 2760 update_overall_status($php_properties, "PHP"); 2761 update_overall_status($security, "PHP Security"); 2762 update_overall_status($tiki_security, "Tiki Security"); 2763 $return = json_encode($monitoring_info); 2764 echo $return; 2765} else { // not stand-alone 2766 if (isset($_REQUEST['acknowledge']) || empty($last_state)) { 2767 $tiki_check_status = array(); 2768 function process_acks(&$check_group, $check_group_name) 2769 { 2770 global $tiki_check_status; 2771 foreach ($check_group as $key => $value) { 2772 $formkey = str_replace(array('.',' '), '_', $key); 2773 if (isset($check_group["$key"]['fitness']) && ($check_group["$key"]['fitness'] === 'good' || $check_group["$key"]['fitness'] === 'safe') || 2774 (isset($_REQUEST["$formkey"]) && $_REQUEST["$formkey"] === "on")) { 2775 $check_group["$key"]['ack'] = true; 2776 } else { 2777 $check_group["$key"]['ack'] = false; 2778 } 2779 } 2780 $tiki_check_status["$check_group_name"] = $check_group; 2781 } 2782 process_acks($mysql_properties, 'MySQL'); 2783 process_acks($server_properties, 'Server'); 2784 if ($apache_properties) { 2785 process_acks($apache_properties, "Apache"); 2786 } 2787 if ($iis_properties) { 2788 process_acks($iis_properties, "IIS"); 2789 } 2790 process_acks($php_properties, "PHP"); 2791 process_acks($security, "PHP Security"); 2792 $json_tiki_check_status = json_encode($tiki_check_status); 2793 $query = "INSERT INTO tiki_preferences (`name`, `value`) values('tiki_check_status', ? ) on duplicate key update `value`=values(`value`)"; 2794 $bindvars = array($json_tiki_check_status); 2795 $result = $tikilib->query($query, $bindvars); 2796 } 2797 $smarty->assign_by_ref('server_information', $server_information); 2798 $smarty->assign_by_ref('server_properties', $server_properties); 2799 $smarty->assign_by_ref('mysql_properties', $mysql_properties); 2800 $smarty->assign_by_ref('php_properties', $php_properties); 2801 $smarty->assign_by_ref('php_sapi_info', $php_sapi_info); 2802 if ($apache_properties) { 2803 $smarty->assign_by_ref('apache_properties', $apache_properties); 2804 } else { 2805 $smarty->assign('no_apache_properties', 'You are either not running the preferred Apache web server or you are running PHP with a SAPI that does not allow checking Apache properties (e.g. CGI or FPM).'); 2806 } 2807 if ($iis_properties) { 2808 $smarty->assign_by_ref('iis_properties', $iis_properties); 2809 } else { 2810 $smarty->assign('no_iis_properties', 'You are not running IIS web server.'); 2811 } 2812 $smarty->assign_by_ref('security', $security); 2813 $smarty->assign_by_ref('mysql_variables', $mysql_variables); 2814 $smarty->assign_by_ref('mysql_crashed_tables', $mysql_crashed_tables); 2815 if ($prefs['fgal_enable_auto_indexing'] === 'y') { 2816 $smarty->assign_by_ref('file_handlers', $file_handlers); 2817 } 2818 // disallow robots to index page: 2819 2820 $fmap = array( 2821 'good' => array('icon' => 'ok', 'class' => 'success'), 2822 'safe' => array('icon' => 'ok', 'class' => 'success'), 2823 'bad' => array('icon' => 'ban', 'class' => 'danger'), 2824 'unsafe' => array('icon' => 'ban', 'class' => 'danger'), 2825 'risky' => array('icon' => 'warning', 'class' => 'warning'), 2826 'unsure' => array('icon' => 'warning', 'class' => 'warning'), 2827 'info' => array('icon' => 'information', 'class' => 'info'), 2828 'unknown' => array('icon' => 'help', 'class' => 'muted'), 2829 ); 2830 $smarty->assign('fmap', $fmap); 2831 2832 if (isset($_REQUEST['bomscanner']) && class_exists('BOMChecker_Scanner')) { 2833 $timeoutLimit = ini_get('max_execution_time'); 2834 if ($timeoutLimit < 120) { 2835 set_time_limit(120); 2836 } 2837 2838 $BOMScanner = new BOMChecker_Scanner(); 2839 $BOMFiles = $BOMScanner->scan(); 2840 $BOMTotalScannedFiles = $BOMScanner->getScannedFiles(); 2841 2842 $smarty->assign('bom_total_files_scanned', $BOMTotalScannedFiles); 2843 $smarty->assign('bom_detected_files', $BOMFiles); 2844 $smarty->assign('bomscanner', true); 2845 } 2846 2847 $smarty->assign('trim_capable', $trimCapable); 2848 if ($trimCapable) { 2849 $smarty->assign('trim_server_requirements', $trimServerRequirements); 2850 $smarty->assign('trim_client_requirements', $trimClientRequirements); 2851 } 2852 2853 $smarty->assign('sensitive_data_detected_files', $sensitiveDataDetectedFiles); 2854 2855 $smarty->assign('benchmark', $benchmark); 2856 $smarty->assign('users_with_mcrypt_data', $usersWithMCrypt); 2857 $smarty->assign('metatag_robots', 'NOINDEX, NOFOLLOW'); 2858 $smarty->assign('mid', 'tiki-check.tpl'); 2859 $smarty->display('tiki.tpl'); 2860} 2861 2862/** 2863 * Check package warnings based on specific nuances of each package 2864 * @param $warnings 2865 * @param $package 2866 */ 2867function checkPackageWarnings(&$warnings, $package) 2868{ 2869 switch ($package['name']) { 2870 case 'media-alchemyst/media-alchemyst': 2871 if (! AlchemyLib::hasReadWritePolicies()) { 2872 $warnings[] = tr( 2873 'Alchemy requires "Read" and "Write" policy rights. More info: <a href="%0" target="_blank">%1</a>', 2874 'https://doc.tiki.org/tiki-index.php?page=Media+Alchemyst#Document_to_Image_issues', 2875 'Media Alchemyst - Document to Image issues' 2876 ); 2877 } 2878 2879 break; 2880 } 2881} 2882 2883/** 2884 * Check if paths set in preferences exist in the system, or if classes exist in project/system 2885 * 2886 * @param array $preferences An array with preference key and preference info 2887 * 2888 * @return array An array with warning messages. 2889 */ 2890function checkPreferences(array $preferences) 2891{ 2892 global $prefs; 2893 2894 $warnings = array(); 2895 2896 foreach ($preferences as $prefKey => $pref) { 2897 if ($pref['type'] == 'path') { 2898 if (isset($prefs[$prefKey]) && ! file_exists($prefs[$prefKey])) { 2899 $warnings[] = tr("The path '%0' on preference '%1' does not exist", $prefs[$prefKey], $pref['name']); 2900 } 2901 } elseif ($pref['type'] == 'classOptions') { 2902 if (isset($prefs[$prefKey])) { 2903 $options = $pref['options'][$prefs[$prefKey]]; 2904 2905 if (! empty($options['classLib']) && ! class_exists($options['classLib'])) { 2906 $warnings[] = tr("The lib '%0' on preference '%1', option '%2' does not exist", $options['classLib'], $pref['name'], $options['name']); 2907 } 2908 2909 if (! empty($options['className']) && ! class_exists($options['className'])) { 2910 $warnings[] = tr("The class '%0' needed for preference '%1', with option '%2' selected, does not exist", $options['className'], $pref['name'], $options['name']); 2911 } 2912 2913 if (! empty($options['extension']) && ! extension_loaded($options['extension'])) { 2914 $warnings[] = tr("The extension '%0' on preference '%1', with option '%2' selected, is not loaded", $options['extension'], $pref['name'], $options['name']); 2915 } 2916 } 2917 } 2918 } 2919 2920 return $warnings; 2921} 2922 2923/** 2924 * Check if a given command can be located in the system 2925 * 2926 * @param $command 2927 * @return bool true if available, false if not. 2928 */ 2929function commandIsAvailable($command) 2930{ 2931 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 2932 $template = "where %s"; 2933 } else { 2934 $template = "command -v %s 2>/dev/null"; 2935 } 2936 2937 $returnCode = ''; 2938 if (function_exists('exec')) { 2939 exec(sprintf($template, escapeshellarg($command)), $output, $returnCode); 2940 } 2941 2942 return $returnCode === 0 ? true : false; 2943} 2944 2945/** 2946 * Script to benchmark PHP and MySQL 2947 * @see https://github.com/odan/benchmark-php 2948 */ 2949class BenchmarkPhp 2950{ 2951 /** 2952 * Executes the benchmark and returns an array in the format expected by renderTable 2953 * @return array Benchmark results 2954 */ 2955 public static function run() 2956 { 2957 set_time_limit(120); // 2 minutes 2958 2959 global $host_tiki, $dbs_tiki, $user_tiki, $pass_tiki; 2960 2961 $options = array(); 2962 2963 $options['db.host'] = $host_tiki; 2964 $options['db.user'] = $user_tiki; 2965 $options['db.pw'] = $pass_tiki; 2966 $options['db.name'] = $dbs_tiki; 2967 2968 $benchmarkResult = self::test_benchmark($options); 2969 2970 $benchmark = $benchmarkResult['benchmark']; 2971 if (isset($benchmark['mysql'])) { 2972 foreach ($benchmark['mysql'] as $k => $v) { 2973 $benchmark['mysql.' . $k] = $v; 2974 } 2975 unset($benchmark['mysql']); 2976 } 2977 $benchmark['total'] = $benchmarkResult['total']; 2978 $benchmark = array_map( 2979 function ($v) { 2980 return array('value' => $v); 2981 }, 2982 $benchmark 2983 ); 2984 2985 return $benchmark; 2986 } 2987 2988 /** 2989 * Execute the benchmark 2990 * @param $settings database connection settings 2991 * @return array Benchmark results 2992 */ 2993 protected static function test_benchmark($settings) 2994 { 2995 $timeStart = microtime(true); 2996 2997 $result = array(); 2998 $result['version'] = '1.1'; 2999 $result['sysinfo']['time'] = date("Y-m-d H:i:s"); 3000 $result['sysinfo']['php_version'] = PHP_VERSION; 3001 $result['sysinfo']['platform'] = PHP_OS; 3002 $result['sysinfo']['server_name'] = $_SERVER['SERVER_NAME']; 3003 $result['sysinfo']['server_addr'] = $_SERVER['SERVER_ADDR']; 3004 3005 self::test_math($result); 3006 self::test_string($result); 3007 self::test_loops($result); 3008 self::test_ifelse($result); 3009 if (isset($settings['db.host']) && function_exists('mysqli_connect')) { 3010 self::test_mysql($result, $settings); 3011 } 3012 3013 $result['total'] = self::timer_diff($timeStart); 3014 return $result; 3015 } 3016 3017 /** 3018 * Benchmark the execution of multiple math functions 3019 * @param $result Benchmark results 3020 * @param int $count Number of iterations 3021 */ 3022 protected static function test_math(&$result, $count = 99999) 3023 { 3024 $timeStart = microtime(true); 3025 3026 $mathFunctions = array( 3027 "abs", 3028 "acos", 3029 "asin", 3030 "atan", 3031 "bindec", 3032 "floor", 3033 "exp", 3034 "sin", 3035 "tan", 3036 "pi", 3037 "is_finite", 3038 "is_nan", 3039 "sqrt", 3040 ); 3041 for ($i = 0; $i < $count; $i++) { 3042 foreach ($mathFunctions as $function) { 3043 call_user_func_array($function, array($i)); 3044 } 3045 } 3046 $result['benchmark']['math'] = self::timer_diff($timeStart); 3047 } 3048 3049 /** 3050 * Benchmark the execution of multiple string functions 3051 * @param $result Benchmark results 3052 * @param int $count Number of iterations 3053 */ 3054 protected static function test_string(&$result, $count = 99999) 3055 { 3056 $timeStart = microtime(true); 3057 $stringFunctions = array( 3058 "addslashes", 3059 "chunk_split", 3060 "metaphone", 3061 "strip_tags", 3062 "md5", 3063 "sha1", 3064 "strtoupper", 3065 "strtolower", 3066 "strrev", 3067 "strlen", 3068 "soundex", 3069 "ord", 3070 ); 3071 3072 $string = 'the quick brown fox jumps over the lazy dog'; 3073 for ($i = 0; $i < $count; $i++) { 3074 foreach ($stringFunctions as $function) { 3075 call_user_func_array($function, array($string)); 3076 } 3077 } 3078 $result['benchmark']['string'] = self::timer_diff($timeStart); 3079 } 3080 3081 /** 3082 * Benchmark the execution of loops 3083 * @param $result Benchmark results 3084 * @param int $count Number of iterations 3085 */ 3086 protected static function test_loops(&$result, $count = 999999) 3087 { 3088 $timeStart = microtime(true); 3089 for ($i = 0; $i < $count; ++$i) { 3090 } 3091 $i = 0; 3092 while ($i < $count) { 3093 ++$i; 3094 } 3095 $result['benchmark']['loops'] = self::timer_diff($timeStart); 3096 } 3097 3098 /** 3099 * Benchmark the execution of conditional operators 3100 * @param $result Benchmark results 3101 * @param int $count Number of iterations 3102 */ 3103 protected static function test_ifelse(&$result, $count = 999999) 3104 { 3105 $timeStart = microtime(true); 3106 for ($i = 0; $i < $count; $i++) { 3107 if ($i == -1) { 3108 } elseif ($i == -2) { 3109 } else { 3110 if ($i == -3) { 3111 } 3112 } 3113 } 3114 $result['benchmark']['ifelse'] = self::timer_diff($timeStart); 3115 } 3116 3117 /** 3118 * Benchmark MySQL operations 3119 * @param $result Benchmark results 3120 * @param $settings MySQL connection information 3121 * @return array 3122 */ 3123 protected static function test_mysql(&$result, $settings) 3124 { 3125 $timeStart = microtime(true); 3126 3127 $link = mysqli_connect($settings['db.host'], $settings['db.user'], $settings['db.pw']); 3128 $result['benchmark']['mysql']['connect'] = self::timer_diff($timeStart); 3129 3130 mysqli_select_db($link, $settings['db.name']); 3131 $result['benchmark']['mysql']['select_db'] = self::timer_diff($timeStart); 3132 3133 $dbResult = mysqli_query($link, 'SELECT VERSION() as version;'); 3134 $arr_row = mysqli_fetch_array($dbResult); 3135 $result['sysinfo']['mysql_version'] = $arr_row['version']; 3136 $result['benchmark']['mysql']['query_version'] = self::timer_diff($timeStart); 3137 3138 $query = "SELECT BENCHMARK(1000000,ENCODE('hello',RAND()));"; 3139 $dbResult = mysqli_query($link, $query); 3140 $result['benchmark']['mysql']['query_benchmark'] = self::timer_diff($timeStart); 3141 3142 mysqli_close($link); 3143 3144 $result['benchmark']['mysql']['total'] = self::timer_diff($timeStart); 3145 return $result; 3146 } 3147 3148 /** 3149 * Helper to calculate time elapsed 3150 * @param $timeStart time to compare against now 3151 * @return string time elapsed 3152 */ 3153 protected static function timer_diff($timeStart) 3154 { 3155 return number_format(microtime(true) - $timeStart, 3); 3156 } 3157} 3158 3159/** 3160 * Identify files, like backup copies made by editors, or manual copies of the local.php files, 3161 * that may be accessed remotely and, because they are not interpreted as PHP, may expose the source, 3162 * which might contain credentials or other sensitive information. 3163 * Ref: http://feross.org/cmsploit/ 3164 * 3165 * @param array $files Array of filenames. Suspicious files will be added to this array. 3166 * @param string $sourceDir Path of the directory to check 3167 */ 3168function check_for_remote_readable_files(array &$files, $sourceDir = 'db') 3169{ 3170 //fix dir slash 3171 $sourceDir = str_replace('\\', '/', $sourceDir); 3172 3173 if (substr($sourceDir, -1, 1) != '/') { 3174 $sourceDir .= '/'; 3175 } 3176 3177 if (! is_dir($sourceDir)) { 3178 return; 3179 } 3180 3181 $sourceDirHandler = opendir($sourceDir); 3182 3183 if ($sourceDirHandler === false) { 3184 return; 3185 } 3186 3187 while ($file = readdir($sourceDirHandler)) { 3188 // Skip ".", ".." 3189 if ($file == '.' || $file == '..') { 3190 continue; 3191 } 3192 3193 $sourceFilePath = $sourceDir . $file; 3194 3195 if (is_dir($sourceFilePath)) { 3196 check_for_remote_readable_files($files, $sourceFilePath); 3197 } 3198 3199 if (! is_file($sourceFilePath)) { 3200 continue; 3201 } 3202 3203 $pattern = '/(^#.*#|~|.sw[op])$/'; 3204 preg_match($pattern, $file, $matches); 3205 3206 if (! empty($matches[1])) { 3207 $files[] = $file; 3208 continue; 3209 } 3210 3211 // Match "local.php.bak", "local.php.bck", "local.php.save", "local.php." or "local.txt", for example 3212 $pattern = '/local(?!.*[.]php$).*$/'; // The negative lookahead prevents local.php and other files which will be interpreted as PHP from matching. 3213 preg_match($pattern, $file, $matches); 3214 3215 if (! empty($matches[0])) { 3216 $files[] = $file; 3217 continue; 3218 } 3219 } 3220} 3221 3222function check_isIIS() 3223{ 3224 static $IIS; 3225 // Sample value Microsoft-IIS/7.5 3226 if (! isset($IIS) && isset($_SERVER['SERVER_SOFTWARE'])) { 3227 $IIS = substr($_SERVER['SERVER_SOFTWARE'], 0, 13) == 'Microsoft-IIS'; 3228 } 3229 return $IIS; 3230} 3231 3232function check_hasIIS_UrlRewriteModule() 3233{ 3234 return isset($_SERVER['IIS_UrlRewriteModule']) == true; 3235} 3236 3237/** 3238 * Returns the number of users with data preferences encrypted with mcrypt 3239 * @return bool|int|mixed 3240 */ 3241function check_userPreferencesMCrypt() 3242{ 3243 global $tikilib; 3244 3245 $query = 'SELECT count(DISTINCT user) FROM `tiki_user_preferences` WHERE `prefName` like \'dp.%\''; 3246 return $tikilib->getOne($query); 3247} 3248 3249function get_content_from_url($url) 3250{ 3251 if (function_exists('curl_init') && function_exists('curl_exec')) { 3252 $curl = curl_init(); 3253 curl_setopt($curl, CURLOPT_URL, $url); 3254 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 3255 curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); 3256 if (isset($_SERVER) && isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) { 3257 curl_setopt($curl, CURLOPT_USERPWD, $_SERVER['PHP_AUTH_USER'] . ":" . $_SERVER['PHP_AUTH_PW']); 3258 } 3259 $content = curl_exec($curl); 3260 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); 3261 if ($http_code != 200) { 3262 $content = "fail-http-" . $http_code; 3263 } 3264 curl_close($curl); 3265 } else { 3266 $content = "fail-no-request-done"; 3267 } 3268 return $content; 3269} 3270 3271function createPage($title, $content) 3272{ 3273 echo <<<END 3274<!DOCTYPE html> 3275<html> 3276 <head> 3277 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 3278 <link type="text/css" rel="stylesheet" href="//dev.tiki.org/vendor/twitter/bootstrap/dist/css/bootstrap.css" /> 3279 <title>$title</title> 3280 <style type="text/css"> 3281 table { border-collapse: collapse;} 3282 #middle { margin: 0 auto; } 3283 .button { 3284 border-radius: 3px 3px 3px 3px; 3285 font-size: 12.05px; 3286 font-weight: bold; 3287 padding: 2px 4px 3px; 3288 text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 3289 color: #FFF; 3290 text-transform: uppercase; 3291 } 3292 .unsure {background: #f89406;} 3293 .bad, .risky { background-color: #bd362f;} 3294 .good, .safe { background-color: #5bb75b;} 3295 .info {background-color: #2f96b4;} 3296// h1 { border-bottom: 1px solid #DADADA; color: #7e7363; } 3297 </style> 3298 </head> 3299 <body class="tiki_wiki "> 3300 <div id="fixedwidth" > 3301 <div class="header_outer"> 3302 <div class="header_container"> 3303 <div class="clearfix "> 3304 <header id="header" class="header"> 3305 <div class="content clearfix modules" id="top_modules" style="min-height: 168px;"> 3306 <div class="sitelogo" style="float: left"> 3307END; 3308 echo tikiLogo(); 3309 echo <<< END 3310 </div> 3311 <div class="sitetitles" style="float: left;"> 3312 <div class="sitetitle" style="font-size: 42px;">$title</div> 3313 </div> 3314 </div> 3315 </header> 3316 </div> 3317 </div> 3318 </div> 3319 <div class="middle_outer"> 3320 <div id="middle" > 3321 <div class="topbar clearfix"> 3322 <h1 style="font-size: 30px; line-height: 30px; color: #fff; text-shadow: 3px 2px 0 #781437; margin: 8px 0 0 10px; padding: 0;"> 3323 </h1> 3324 </div> 3325 </div> 3326 <div id="middle" > 3327 $content 3328 </div> 3329 </div> 3330 </div> 3331 <footer id="footer" class="footer" style="margin-top: 50px;"> 3332 <div class="footer_liner"> 3333 <div class="footerbgtrap" style="padding: 10px 0;"> 3334 <a href="http://tiki.org" target="_blank" title="Powered by Tiki Wiki CMS Groupware"> 3335END; 3336 echo tikiButton(); 3337 echo <<< END 3338 <img src="img/tiki/tikibutton.png" alt="Powered by Tiki Wiki CMS Groupware" /> 3339 </a> 3340 </div> 3341 </div> 3342</footer> 3343</div> 3344 </body> 3345</html> 3346END; 3347 die; 3348} 3349 3350function tikiLogo() 3351{ 3352 return '<img alt="Tiki Logo" src="data:image/png;base64,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%3D" />'; 3353} 3354 3355function tikiButton() 3356{ 3357 return '<img alt="tikibutton" src="data:image/png;base64,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%3D%3D"'; 3358} 3359 3360function no_cache_found() 3361{ 3362 global $php_properties; 3363 3364 if (check_isIIS()) { 3365 $php_properties['ByteCode Cache'] = array( 3366 'fitness' => tra('info'), 3367 'setting' => 'N/A', 3368 'message' => tra('Neither APC, WinCache nor xCache is being used as the ByteCode Cache; if one of these were used and correctly configured, performance would be increased. See Admin->Performance in the Tiki for more details.') 3369 ); 3370 } else { 3371 $php_properties['ByteCode Cache'] = array( 3372 'fitness' => tra('info'), 3373 'setting' => 'N/A', 3374 'message' => tra('Neither APC, xCache, nor OPcache is being used as the ByteCode Cache; if one of these were used and correctly configured, performance would be increased. See Admin->Performance in the Tiki for more details.') 3375 ); 3376 } 3377} 3378