Case folding of property names
[davical.git] / htdocs / setup.php
blobac43670c554ff4e8383b634934446d252866a0de
1 <?php
2 /** todo work out something more than true/false returns for dependency checks */
5 function i18n($value) {
6 return $value; /* Just pass the value through */
9 $skip_errors = false; // We need to hide a couple of unsightly errors even here...
10 function 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);
16 function catch_setup_errors($errno , $errstr , $errfile , $errline , $errcontext ) {
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;
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;
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;
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();
36 catch( Exception $ignored ) {}
37 echo "Error [".$errno."] ".$errstr."\n";
38 echo "At line ", $errline, " of ", $errfile, "\n";
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']:'') );
48 set_error_handler('catch_setup_errors', E_ALL);
50 class CheckResult {
51 private $ok;
52 private $use_class;
53 private $description;
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'));
61 public function getClass() {
62 return $this->use_class;
65 public function setClass( $new_class ) {
66 $this->use_class = $new_class;
69 public function getOK() {
70 return $this->ok;
73 public function getDescription() {
74 return translate($this->description);
77 public function setDescription( $new_desc ) {
78 $this->description = $new_desc;
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.
87 function check_pgsql() {
88 return new CheckResult(function_exists('pg_connect'));
91 function check_pdo() {
92 return new CheckResult(class_exists('PDO'));
95 function check_pdo_pgsql() {
96 global $loaded_extensions;
98 if ( !check_pdo() ) return new CheckResult(false);
99 return new CheckResult(isset($loaded_extensions['pdo_pgsql']));
102 function check_database_connection() {
103 global $c;
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) );
109 function check_gettext() {
110 global $phpinfo, $loaded_extensions;
112 if ( !function_exists('gettext') ) return new CheckResult(false);
113 return new CheckResult(isset($loaded_extensions['gettext']));
116 function check_iconv() {
117 global $phpinfo, $loaded_extensions;
119 if ( !function_exists('iconv') ) return new CheckResult(false);
120 return new CheckResult(isset($loaded_extensions['iconv']));
123 function check_ldap() {
124 global $phpinfo, $loaded_extensions;
126 if (!function_exists('ldap_connect')) return new CheckResult(false);
127 return new CheckResult(isset($loaded_extensions['ldap']));
130 function 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);
137 function check_calendar() {
138 global $phpinfo, $loaded_extensions;
140 if (!function_exists('cal_days_in_month')) return new CheckResult(false);
141 return new CheckResult(isset($loaded_extensions['calendar']));
144 function check_suhosin_server_strip() {
145 global $loaded_extensions;
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') == "" );
153 function check_magic_quotes_gpc() {
154 return new CheckResult( (get_magic_quotes_gpc() == 0) );
157 function check_magic_quotes_runtime() {
158 return new CheckResult( (get_magic_quotes_runtime() == 0) );
161 function check_curl() {
162 global $phpinfo, $loaded_extensions;
164 if (!function_exists('curl_init')) return new CheckResult(false);
165 return new CheckResult(isset($loaded_extensions['curl']));
168 $loaded_extensions = array_flip(get_loaded_extensions());
171 function 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'>%s</p>", $errormessage );
177 if ( !check_gettext()->getOK() ) do_error("The GNU 'gettext' extension for PHP is not available.");
178 if ( !check_pgsql()->getOK() ) do_error("PHP 'pgsql' functions are not available");
179 if ( !check_pdo()->getOK() ) do_error("PHP 'PDO' module is not available");
180 if ( !check_pdo_pgsql()->getOK() ) do_error("The PDO drivers for PostgreSQL are not available");
181 if ( !check_database_connection()->getOK() ) do_error("Unable to connect to database");
182 if ( !check_iconv()->getOK() ) do_error("The 'iconv' extension for PHP is not available");
184 function get_phpinfo() {
185 ob_start( );
186 phpinfo();
187 $phpinfo = ob_get_contents( );
188 ob_end_clean( );
190 $phpinfo = preg_replace( '{^.*?<body>}s', '', $phpinfo);
191 $phpinfo = preg_replace( '{</body>.*?$}s', '', $phpinfo);
192 return $phpinfo;
194 $phpinfo = get_phpinfo();
196 try {
197 include("./always.php");
198 set_error_handler('log_setup_error', E_ALL);
199 include("DAViCalSession.php");
200 if ( check_pgsql()->GetOK() ) {
201 $session->LoginRequired( (isset($c->restrict_setup_to_admin) && $c->restrict_setup_to_admin ? 'Admin' : null ) );
204 catch( Exception $e ) {
205 class FakeSession {
206 function AllowedTo() {
207 return true;
210 $session = new FakeSession(1);
214 include("interactive-page.php");
215 include("page-header.php");
217 require_once("AwlQuery.php");
220 function check_datetime() {
221 if ( class_exists('DateTime') ) return new CheckResult(true);
222 $result = new CheckResult(false);
223 $result->setClass('dep_warning');
224 $result->setDescription(i18n('Most of DAViCal will work but upgrading to PHP 5.2 or later is strongly recommended.'));
225 return $result;
228 function check_schema_version() {
229 global $c;
230 if ( $c->want_dbversion[0] == $c->schema_major
231 && $c->want_dbversion[1] == $c->schema_minor
232 && $c->want_dbversion[2] == $c->schema_patch ) {
233 return new CheckResult( true );
235 $result = new CheckResult(false);
236 if ( $c->want_dbversion[0] < $c->schema_major
237 || ($c->want_dbversion[0] == $c->schema_major && $c->want_dbversion[1] < $c->schema_minor)
238 || ($c->want_dbversion[0] == $c->schema_major
239 && $c->want_dbversion[1] == $c->schema_minor
240 && $c->want_dbversion[2] < $c->schema_patch)
243 $result->setClass('dep_warning');
245 $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), implode('.',$c->want_dbversion),
246 $c->schema_major.'.'.$c->schema_minor.'.'.$c->schema_patch));
247 return $result;
250 function check_davical_version() {
251 global $c;
252 $url = 'http://www.davical.org/current_davical_version?v='.$c->version_string;
253 $version_file = @fopen($url, 'r');
254 if ( ! $version_file ) return new CheckResult( false, translate("Could not retrieve") . " '$url'", 'dep_warning' );
255 $current_version = htmlentities( trim(fread( $version_file,12)) );
256 fclose($version_file);
257 $result = new CheckResult($c->version_string == $current_version);
258 if ( ! $result->getOK() ) {
259 if ( $c->version_string > $current_version ) {
260 $result->setClass('dep_ok');
261 $result->setDescription( sprintf(i18n('Stable: %s, We have: %s !'), $current_version, $c->version_string) );
263 else {
264 $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), $current_version, $c->version_string) );
267 return $result;
271 function check_awl_version() {
272 global $c;
274 if ( !function_exists('awl_version') ) return new CheckResult(false);
276 $result = new CheckResult($c->want_awl_version == awl_version());
277 if ( ! $result->getOK() ) {
278 $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), $c->want_awl_version, awl_version()) );
279 if ( $c->want_awl_version < awl_version() ) $result->setClass('dep_warning');
281 return $result;
286 function build_site_statistics() {
287 $principals = translate('No. of Principals');
288 $collections = translate('No. of Collections');
289 $resources = translate('No. of Resources');
290 $table = <<<EOTABLE
291 <table class="statistics">
292 <tr><th>$principals</th><th>$collections</th><th>$resources</th></tr>
293 <tr>%s</tr>
294 </table>
295 EOTABLE;
297 if ( !check_database_connection() ) {
298 return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>');
300 $sql = 'SELECT
301 (SELECT count(1) FROM principal) AS principals,
302 (SELECT count(1) FROM collection) AS collections,
303 (SELECT count(1) FROM caldav_data) AS resources';
304 $qry = new AwlQuery($sql);
305 if ( $qry->Exec('setup',__LINE__,__FILE__) && $s = $qry->Fetch() ) {
306 $row = sprintf('<td align="center">%s</td><td align="center">%s</td><td align="center">%s</td>',
307 $s->principals, $s->collections, $s->resources );
308 return sprintf( $table, $row );
310 return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>');
314 function build_dependencies_table( ) {
315 global $c;
317 $dependencies = array(
318 translate('Current DAViCal version ') => 'check_davical_version',
319 translate('AWL Library version ') => 'check_awl_version',
320 translate('PHP not using Apache Filter mode') => 'check_real_php',
321 translate('PHP PDO module available') => 'check_pdo',
322 translate('PDO PostgreSQL drivers') => 'check_pdo_pgsql',
323 translate('Database is Connected') => 'check_database_connection',
324 translate('DAViCal DB Schema version ') => 'check_schema_version',
325 translate('GNU gettext support') => 'check_gettext',
326 translate('PHP iconv support') => 'check_iconv',
327 translate('PHP DateTime class') => 'check_datetime',
328 translate('Suhosin "server.strip" disabled') => 'check_suhosin_server_strip',
329 translate('PHP Magic Quotes GPC off') => 'check_magic_quotes_gpc',
330 translate('PHP Magic Quotes runtime off') => 'check_magic_quotes_runtime',
331 translate('PHP calendar extension available') => 'check_calendar',
332 translate('PHP curl support') => 'check_curl'
335 if ( isset($c->authenticate_hook) && isset($c->authenticate_hook['call']) && $c->authenticate_hook['call'] == 'LDAP_check') {
336 $dependencies[translate('PHP LDAP module available')] = 'check_ldap';
339 $dependencies_table = '';
340 $dep_tpl = '<tr class="%s">
341 <td>%s</td>
342 <td>%s</td>
343 <td><a href="http://wiki.davical.org/w/Setup_Failure_Codes/%s">Explanation on DAViCal Wiki</a></td>
344 </tr>
346 foreach( $dependencies AS $k => $v ) {
347 $check_result = $v();
348 $dependencies_table .= sprintf( $dep_tpl, $check_result->getClass(),
350 $check_result->getDescription(),
351 rawurlencode($k)
355 return $dependencies_table;
359 $heading_setup = translate('Setup');
360 $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.');
363 $want_dbversion = implode('.',$c->want_dbversion);
364 $heading_versions = translate('Current Versions');
365 if ( check_schema_version() != true )
367 $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.');
368 $paragraph_versions = sprintf( $paragraph_versions, $c->version_string, $want_dbversion, $c->schema_major, $c->schema_minor, $c->schema_patch);
369 } else {
370 $paragraph_versions = translate('You are currently running DAViCal version %s. The database schema is at version %d.%d.%d.');
371 $paragraph_versions = sprintf( $paragraph_versions, $c->version_string, $c->schema_major, $c->schema_minor, $c->schema_patch);
375 $heading_dependencies = translate('Dependencies');
376 $th_dependency = translate('Dependency');
377 $th_status = translate('Status');
378 $dependencies_table = build_dependencies_table();
380 $heading_site_statistics = translate('Site Statistics');
381 if ( check_database_connection()->GetOK() ) {
382 try {
383 $site_statistics_table = build_site_statistics();
385 catch( Exception $e ) {
386 $site_statistics_table = "Statistics unavailable";
389 else {
390 $site_statistics_table = "Statistics unavailable";
393 $heading_config_clients = translate('Configuring Calendar Clients for DAViCal');
394 $heading_config_davical = translate('Configuring DAViCal');
395 $davical_configuration_errors = ( $config_warnings == '' ? '' : '<div class="error"><h3 class="error">'
396 . translate('Your configuration produced PHP errors which should be corrected') . '</h3><pre>'
397 . $config_warnings.'</pre></div>'
401 echo <<<EOBODY
402 <style>
403 tr.dep_ok {
404 background-color:#80ff80;
406 tr.dep_fail {
407 background-color:#ff8080;
409 tr.dep_warning {
410 background-color:#ffb040;
412 table, table.dependencies {
413 border: 1px grey solid;
414 border-collapse: collapse;
415 padding: 0.1em;
416 margin: 0 1em 1.5em;
418 table tr td, table tr th, table.dependencies tr td, table.dependencies tr th {
419 border: 1px grey solid;
420 padding: 0.1em 0.2em;
423 padding: 0.3em 0.2em 0.7em;
425 </style>
427 <h1>$heading_setup</h1>
428 <p>$paragraph_setup
430 <h2>$heading_dependencies</h2>
432 <table class="dependencies">
433 <tr>
434 <th>$th_dependency</th>
435 <th>$th_status</th>
436 </tr>
437 $dependencies_table
438 </table>
439 </p>
440 <h2>$heading_config_davical</h2>
441 <p>If you can read this then things must be mostly working already.</p>
442 $davical_configuration_errors
443 <p>The <a href="http://www.davical.org/installation.php">installation page on the DAViCal website</a> has
444 some further information on how to install and configure this application.</p>
446 <h2>$heading_config_clients</h2>
447 <p>The <a href="http://www.davical.org/clients.php">client setup page on the DAViCal website</a> has information on how
448 to configure Evolution, Sunbird, Lightning and Mulberry to use remotely hosted calendars.</p>
449 <p>The administrative interface has no facility for viewing or modifying calendar data.</p>
451 <h2>$heading_site_statistics</h2>
452 <p>$site_statistics_table</p>
454 <h2>PHP Information</h2>
455 <script language="javascript">
456 function toggle_visible() {
457 var argv = toggle_visible.arguments;
458 var argc = argv.length;
460 var fld_checkbox = document.getElementById(argv[0]);
462 if ( argc < 2 ) {
463 return;
466 for (var i = 1; i < argc; i++) {
467 var block_id = argv[i].substr(1);
468 var block_logical = argv[i].substr(0,1);
469 var b = document.getElementById(block_id);
470 if ( block_logical == '!' )
471 b.style.display = (fld_checkbox.checked ? 'none' : '');
472 else
473 b.style.display = (!fld_checkbox.checked ? 'none' : '');
476 </script><p><label>Show phpinfo() output:<input type="checkbox" value="1" id="fld_show_phpinfo" onclick="toggle_visible('fld_show_phpinfo','=phpinfo')"></label></p>
477 <div style="display:none" id="phpinfo">$phpinfo</div>
479 EOBODY;
481 include("page-footer.php");