1<?php 2/** todo work out something more than true/false returns for dependency checks */ 3 4 5function i18n($value) { 6 return $value; /* Just pass the value through */ 7} 8 9$skip_errors = false; // We need to hide a couple of unsightly errors even here... 10function log_setup_error($errno , $errstr , $errfile , $errline) { 11 global $skip_errors; 12 if ( $skip_errors ) return; 13 error_log('DAViCal setup.php: Informational: '.$errfile.'('.$errline.'): ['.$errno.'] '.$errstr); 14} 15 16function catch_setup_errors($errno , $errstr , $errfile , $errline , $errcontext = null ) { 17 if ( $errno == 2 ) { 18 // A working installation will regularly fail to include_once() for several files as it searches for the location 19 log_setup_error($errno , $errstr , $errfile , $errline); 20 return true; 21 } 22 if ( $errno == 8 ) { 23 // Yeah, OK, so we redundantly call ob_flash() without needing to... 24 log_setup_error($errno , $errstr , $errfile , $errline); 25 return true; 26 } 27 else if ( $errno == 256 ) { 28 // This will (probably) be a database connection error, which will throw an exception if we return. 29 log_setup_error($errno , $errstr , $errfile , $errline); 30 return true; 31 } 32 if ( !headers_sent() ) header("Content-type: text/plain"); else echo "<pre>\n"; 33 try { 34 @ob_flush(); // Seems like it should be better to do the following but is problematic on PHP5.3 at least: while ( ob_get_level() > 0 ) ob_end_flush(); 35 } 36 catch( Exception $ignored ) {} 37 echo "Error [".$errno."] ".$errstr."\n"; 38 echo "At line ", $errline, " of ", $errfile, "\n"; 39 40 $e = new Exception(); 41 $trace = array_reverse($e->getTrace()); 42 echo "================= Stack Trace ===================\n"; 43 foreach( $trace AS $k => $v ) { 44 printf( "%s[%d] %s%s%s()\n", $v['file'], $v['line'], (isset($v['class'])?$v['class']:''), (isset($v['type'])?$v['type']:''), (isset($v['function'])?$v['function']:'') ); 45 } 46} 47 48set_error_handler('catch_setup_errors', E_ALL); 49 50class CheckResult { 51 private $ok; 52 private $use_class; 53 private $description; 54 55 function __construct( $success, $description=null, $use_class=null ) { 56 $this->ok = (boolean) $success; 57 $this->description = (isset($description)?$description : ($success===true? i18n('Passed') : i18n('Fail'))); 58 $this->use_class = (isset($use_class)?$use_class:($success===true?'dep_ok' : 'dep_fail')); 59 } 60 61 public function getClass() { 62 return $this->use_class; 63 } 64 65 public function setClass( $new_class ) { 66 $this->use_class = $new_class; 67 } 68 69 public function getOK() { 70 return $this->ok; 71 } 72 73 public function getDescription() { 74 return translate($this->description); 75 } 76 77 public function setDescription( $new_desc ) { 78 $this->description = $new_desc; 79 } 80 81} 82 83/** 84 * We put many of these checks before we even try to load always.php so that we 85 * can try and do some diagnostic work to ensure it will load OK. 86 */ 87function check_pgsql() { 88 return new CheckResult(function_exists('pg_connect')); 89} 90 91function check_pdo() { 92 return new CheckResult(class_exists('PDO')); 93} 94 95function check_pdo_pgsql() { 96 global $loaded_extensions; 97 98 if ( !check_pdo() ) return new CheckResult(false); 99 return new CheckResult(isset($loaded_extensions['pdo_pgsql'])); 100} 101 102function check_database_connection() { 103 global $c; 104 105 if ( !check_pdo_pgsql() ) return new CheckResult(false); 106 return new CheckResult( !( empty($c->schema_major) || $c->schema_major == 0 || empty($c->schema_minor) || $c->schema_minor == 0) ); 107} 108 109function check_gettext() { 110 global $phpinfo, $loaded_extensions; 111 112 if ( !function_exists('gettext') ) return new CheckResult(false); 113 return new CheckResult(isset($loaded_extensions['gettext'])); 114} 115 116function check_iconv() { 117 global $phpinfo, $loaded_extensions; 118 119 if ( !function_exists('iconv') ) return new CheckResult(false); 120 return new CheckResult(isset($loaded_extensions['iconv'])); 121} 122 123function check_ldap() { 124 global $phpinfo, $loaded_extensions; 125 126 if (!function_exists('ldap_connect')) return new CheckResult(false); 127 return new CheckResult(isset($loaded_extensions['ldap'])); 128} 129 130function check_real_php() { 131 global $phpinfo, $loaded_extensions; 132 // Looking for "Server API </td><td class="v">Apache 2.0 Filter" in the phpinfo 133 if ( preg_match('{Server API.*Apache 2\.. Filter}', $phpinfo) ) return new CheckResult(false); 134 return new CheckResult(true); 135} 136 137function check_calendar() { 138 global $phpinfo, $loaded_extensions; 139 140 if (!function_exists('cal_days_in_month')) return new CheckResult(false); 141 return new CheckResult(isset($loaded_extensions['calendar'])); 142} 143 144function check_suhosin_server_strip() { 145 global $loaded_extensions; 146 147 if ( !isset($loaded_extensions['suhosin']) ) return new CheckResult(true); 148 return new CheckResult( ini_get('suhosin.server.strip') == "0" 149 || strtolower(ini_get('suhosin.server.strip')) == "off" 150 || ini_get('suhosin.server.strip') == "" ); 151} 152 153function check_magic_quotes_gpc() { 154 return new CheckResult( (get_magic_quotes_gpc() == 0) ); 155} 156 157function check_magic_quotes_runtime() { 158 return new CheckResult( (get_magic_quotes_runtime() == 0) ); 159} 160 161function check_curl() { 162 global $phpinfo, $loaded_extensions; 163 164 if (!function_exists('curl_init')) return new CheckResult(false); 165 return new CheckResult(isset($loaded_extensions['curl'])); 166} 167 168$loaded_extensions = array_flip(get_loaded_extensions()); 169 170 171function do_error( $errormessage ) { 172 // We can't translate this because we're testing these things even before 173 // the translation interface is available... 174 printf("<p class='error'><br/>%s</p>", $errormessage ); 175} 176 177if ( !check_gettext()->getOK() ) do_error("The GNU 'gettext' extension for PHP is not available."); 178if ( !check_pgsql()->getOK() ) do_error("PHP 'pgsql' functions are not available"); 179if ( !check_pdo()->getOK() ) do_error("PHP 'PDO' module is not available"); 180if ( !check_pdo_pgsql()->getOK() ) do_error("The PDO drivers for PostgreSQL are not available"); 181if ( !check_iconv()->getOK() ) do_error("The 'iconv' extension for PHP is not available"); 182 183function get_phpinfo() { 184 ob_start( ); 185 phpinfo(); 186 $phpinfo = ob_get_contents( ); 187 ob_end_clean( ); 188 189 $phpinfo = preg_replace( '{^.*?<body>}s', '', $phpinfo); 190 $phpinfo = preg_replace( '{</body>.*?$}s', '', $phpinfo); 191 return $phpinfo; 192} 193$phpinfo = get_phpinfo(); 194 195try { 196 include("./always.php"); 197 set_error_handler('log_setup_error', E_ALL); 198 include("DAViCalSession.php"); 199 if ( check_pgsql()->GetOK() ) { 200 $session->LoginRequired( (isset($c->restrict_setup_to_admin) && $c->restrict_setup_to_admin ? 'Admin' : null ) ); 201 } 202} 203catch( Exception $e ) { 204 class setupFakeSession { 205 function AllowedTo() { 206 return true; 207 } 208 } 209 $session = new setupFakeSession(1); 210} 211 212 213include("interactive-page.php"); 214include("page-header.php"); 215 216require_once("AwlQuery.php"); 217 218 219function check_datetime() { 220 if ( class_exists('DateTime') ) return new CheckResult(true); 221 $result = new CheckResult(false); 222 $result->setClass('dep_warning'); 223 $result->setDescription(i18n('Most of DAViCal will work but upgrading to PHP 5.2 or later is strongly recommended.')); 224 return $result; 225} 226 227function check_xml() { 228 return new CheckResult(function_exists('xml_parser_create_ns')); 229} 230 231function check_schema_version() { 232 global $c; 233 if ( $c->want_dbversion[0] == $c->schema_major 234 && $c->want_dbversion[1] == $c->schema_minor 235 && $c->want_dbversion[2] == $c->schema_patch ) { 236 return new CheckResult( true ); 237 } 238 $result = new CheckResult(false); 239 if ( $c->want_dbversion[0] < $c->schema_major 240 || ($c->want_dbversion[0] == $c->schema_major && $c->want_dbversion[1] < $c->schema_minor) 241 || ($c->want_dbversion[0] == $c->schema_major 242 && $c->want_dbversion[1] == $c->schema_minor 243 && $c->want_dbversion[2] < $c->schema_patch) 244 ) 245 { 246 $result->setClass('dep_warning'); 247 } 248 $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), implode('.',$c->want_dbversion), 249 $c->schema_major.'.'.$c->schema_minor.'.'.$c->schema_patch)); 250 return $result; 251} 252 253function check_davical_version() { 254 global $c; 255 if ( ! ini_get('allow_url_fopen') ) 256 return new CheckResult( false, translate("Cannot determine upstream version, because PHP has set “<a href=\"https://secure.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen\"><code>allow_url_fopen</code></a>” to “<code>FALSE</code>”."), 'dep_warning' ); 257 $url = 'https://www.davical.org/current_davical_version?v='.$c->version_string; 258 $version_file = @fopen($url, 'r'); 259 if ( ! $version_file ) return new CheckResult( false, translate("Could not retrieve") . " '$url'", 'dep_warning' ); 260 $current_version = htmlentities( trim(fread( $version_file,12)) ); 261 fclose($version_file); 262 $result = new CheckResult($c->version_string == $current_version); 263 if ( ! $result->getOK() ) { 264 if ( $c->version_string > $current_version ) { 265 $result->setClass('dep_ok'); 266 $result->setDescription( sprintf(i18n('Stable: %s, We have: %s !'), $current_version, $c->version_string) ); 267 } 268 else { 269 $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), $current_version, $c->version_string) ); 270 } 271 } else { 272 $result->setDescription( sprintf(i18n('Passed: %s'), $c->version_string) ); 273 } 274 return $result; 275} 276 277 278function check_awl_version() { 279 global $c; 280 281 if ( !function_exists('awl_version') ) return new CheckResult(false); 282 283 $result = new CheckResult($c->want_awl_version == awl_version()); 284 if ( ! $result->getOK() ) { 285 $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), $c->want_awl_version, awl_version()) ); 286 if ( $c->want_awl_version < awl_version() ) $result->setClass('dep_warning'); 287 } else { 288 $result->setDescription( sprintf(i18n('Passed: %s'), $c->want_awl_version) ); 289 } 290 return $result; 291 292} 293 294 295function build_site_statistics() { 296 $principals = translate('No. of Principals'); 297 $collections = translate('No. of Collections'); 298 $resources = translate('No. of Resources'); 299 $table = <<<EOTABLE 300<table class="statistics"> 301<tr><th>$principals</th><th>$collections</th><th>$resources</th></tr> 302<tr>%s</tr> 303</table> 304EOTABLE; 305 306 if ( !check_database_connection() ) { 307 return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>'); 308 } 309 $sql = 'SELECT 310(SELECT count(1) FROM principal) AS principals, 311(SELECT count(1) FROM collection) AS collections, 312(SELECT count(1) FROM caldav_data) AS resources'; 313 $qry = new AwlQuery($sql); 314 if ( $qry->Exec('setup',__LINE__,__FILE__) && $s = $qry->Fetch() ) { 315 $row = sprintf('<td align="center">%s</td><td align="center">%s</td><td align="center">%s</td>', 316 $s->principals, $s->collections, $s->resources ); 317 return sprintf( $table, $row ); 318 } 319 return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>'); 320} 321 322 323function build_dependencies_table( ) { 324 global $c; 325 326 $dependencies = array( 327 translate('Current DAViCal version ') => 'check_davical_version', 328 translate('AWL Library version ') => 'check_awl_version', 329 translate('PHP not using Apache Filter mode') => 'check_real_php', 330 translate('PHP PDO module available') => 'check_pdo', 331 translate('PDO PostgreSQL drivers') => 'check_pdo_pgsql', 332 translate('Database is Connected') => 'check_database_connection', 333 translate('DAViCal DB Schema version ') => 'check_schema_version', 334 translate('GNU gettext support') => 'check_gettext', 335 translate('PHP iconv support') => 'check_iconv', 336 translate('PHP DateTime class') => 'check_datetime', 337 translate('PHP XML support') => 'check_xml', 338 translate('Suhosin "server.strip" disabled') => 'check_suhosin_server_strip', 339 translate('PHP Magic Quotes GPC off') => 'check_magic_quotes_gpc', 340 translate('PHP Magic Quotes runtime off') => 'check_magic_quotes_runtime', 341 translate('PHP calendar extension available') => 'check_calendar', 342 translate('PHP curl support') => 'check_curl' 343 ); 344 345 if ( isset($c->authenticate_hook) && isset($c->authenticate_hook['call']) && $c->authenticate_hook['call'] == 'LDAP_check') { 346 $dependencies[translate('PHP LDAP module available')] = 'check_ldap'; 347 } 348 349 $translated_failure_code = translate('<a href="https://wiki.davical.org/w/Setup_Failure_Codes/%s">Explanation on DAViCal Wiki</a>'); 350 351 $dependencies_table = ''; 352 $dep_tpl = '<tr class="%s"> 353 <td>%s</td> 354 <td>%s</td> 355 <td>'.$translated_failure_code.'</td> 356</tr> 357'; 358 foreach( $dependencies AS $k => $v ) { 359 $check_result = $v(); 360 $dependencies_table .= sprintf( $dep_tpl, $check_result->getClass(), 361 $k, 362 $check_result->getDescription(), 363 rawurlencode($k) 364 ); 365 } 366 367 return $dependencies_table; 368} 369 370 371$heading_setup = translate('Setup'); 372$paragraph_setup = translate('This page primarily checks the environment needed for DAViCal to work correctly. Suggestions or patches to make it do more useful stuff will be gratefully received.'); 373 374/* 375$want_dbversion = implode('.',$c->want_dbversion); 376$heading_versions = translate('Current Versions'); 377if ( check_schema_version() != true ) 378{ 379 $paragraph_versions = translate('You are currently running DAViCal version %s. The database schema should be at version %s and it is at version %d.%d.%d.'); 380 $paragraph_versions = sprintf( $paragraph_versions, $c->version_string, $want_dbversion, $c->schema_major, $c->schema_minor, $c->schema_patch); 381} else { 382 $paragraph_versions = translate('You are currently running DAViCal version %s. The database schema is at version %d.%d.%d.'); 383 $paragraph_versions = sprintf( $paragraph_versions, $c->version_string, $c->schema_major, $c->schema_minor, $c->schema_patch); 384} 385*/ 386 387$heading_dependencies = translate('Dependencies'); 388$th_dependency = translate('Dependency'); 389$th_status = translate('Status'); 390$dependencies_table = build_dependencies_table(); 391 392$heading_site_statistics = translate('Site Statistics'); 393if ( check_database_connection()->GetOK() ) { 394 try { 395 $site_statistics_table = build_site_statistics(); 396 } 397 catch( Exception $e ) { 398 $site_statistics_table = translate('Statistics unavailable'); 399 } 400} 401else { 402 $site_statistics_table = translate('Statistics unavailable'); 403} 404 405$heading_php_info = translate('PHP Information'); 406 407// Translations shared with index.php 408$heading_clients = translate('Configuring Calendar Clients for DAViCal'); 409$content_cli1 = translate('The <a href="https://www.davical.org/clients.php">client setup page on the DAViCal website</a> has information on how to configure Evolution, Sunbird, Lightning and Mulberry to use remotely hosted calendars.'); 410$content_cli2 = translate('The administrative interface has no facility for viewing or modifying calendar data.'); 411 412// Translations shared with index.php 413$heading_configure = translate('Configuring DAViCal'); 414$content_config1 = translate('If you can read this then things must be mostly working already.'); 415$content_config2 = ( $config_warnings == '' ? '' : '<div class="error"><h3 class="error">' 416 . translate('Your configuration produced PHP errors which should be corrected') . '</h3><pre>' 417 . $config_warnings.'</pre></div>' 418 ); 419$content_config3 = translate('The <a href="https://www.davical.org/installation.php">DAViCal installation page</a> on the DAViCal website has some further information on how to install and configure this application.'); 420 421 422 echo <<<EOBODY 423<style> 424tr.dep_ok { 425 background-color:#80ff80; 426} 427tr.dep_fail { 428 background-color:#ff8080; 429} 430tr.dep_warning { 431 background-color:#ffb040; 432} 433table, table.dependencies { 434 border: 1px grey solid; 435 border-collapse: collapse; 436 padding: 0.1em; 437 margin: 0 1em 1.5em; 438} 439table tr td, table tr th, table.dependencies tr td, table.dependencies tr th { 440 border: 1px grey solid; 441 padding: 0.1em 0.2em; 442} 443p { 444 padding: 0.3em 0.2em 0.7em; 445} 446</style> 447 448<h1>$heading_setup</h1> 449<p>$paragraph_setup 450 451<h2>$heading_dependencies</h2> 452<p> 453<table class="dependencies"> 454<tr> 455<th>$th_dependency</th> 456<th>$th_status</th> 457</tr> 458$dependencies_table 459</table> 460</p> 461<h2>$heading_configure</h2> 462<p>$content_config1</p> 463$content_config2 464<p>$content_config3</p> 465 466<h2>$heading_clients</h2> 467<p>$content_cli1</p> 468<p>$content_cli2</p> 469 470<h2>$heading_site_statistics</h2> 471<p>$site_statistics_table</p> 472 473<h2>$heading_php_info</h2> 474<script language="javascript"> 475function toggle_visible() { 476 var argv = toggle_visible.arguments; 477 var argc = argv.length; 478 479 var fld_checkbox = document.getElementById(argv[0]); 480 481 if ( argc < 2 ) { 482 return; 483 } 484 485 for (var i = 1; i < argc; i++) { 486 var block_id = argv[i].substr(1); 487 var block_logical = argv[i].substr(0,1); 488 var b = document.getElementById(block_id); 489 if ( block_logical == '!' ) 490 b.style.display = (fld_checkbox.checked ? 'none' : ''); 491 else 492 b.style.display = (!fld_checkbox.checked ? 'none' : ''); 493 } 494} 495</script><p><label>Show phpinfo() output:<input type="checkbox" value="1" id="fld_show_phpinfo" onclick="toggle_visible('fld_show_phpinfo','=phpinfo')"></label></p> 496<div style="display:none" id="phpinfo">$phpinfo</div> 497 498EOBODY; 499 500include("page-footer.php"); 501