When a bind is made to an existing bind, bind to the target of that.
[davical.git] / htdocs / setup.php
blob1d6ef178cfe5460b23c87680df490ef9b223be42
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 function log_setup_error($errno , $errstr , $errfile , $errline) {
10 error_log('DAViCal setup.php: Informational: '.$errfile.'('.$errline.'): ['.$errno.'] '.$errstr);
13 function catch_setup_errors($errno , $errstr , $errfile , $errline , $errcontext ) {
14 if ( $errno == 2 ) {
15 // A working installation will regularly fail to include_once() for several files as it searches for the location
16 log_setup_error($errno , $errstr , $errfile , $errline);
17 return true;
19 if ( $errno == 8 ) {
20 // Yeah, OK, so we redundantly call ob_flash() without needing to...
21 log_setup_error($errno , $errstr , $errfile , $errline);
22 return true;
24 else if ( $errno == 256 ) {
25 // This will (probably) be a database connection error, which will throw an exception if we return.
26 log_setup_error($errno , $errstr , $errfile , $errline);
27 return true;
29 if ( !headers_sent() ) header("Content-type: text/plain"); else echo "<pre>\n";
30 while ( ob_get_level() > 0 ) ob_end_flush();
31 echo "Error [".$errno."] ".$errstr."\n";
32 echo "At line ", $errline, " of ", $errfile, "\n";
34 $e = new Exception();
35 $trace = array_reverse($e->getTrace());
36 echo "================= Stack Trace ===================\n";
37 foreach( $trace AS $k => $v ) {
38 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']:'') );
42 set_error_handler('catch_setup_errors', E_ALL);
44 class CheckResult {
45 private $ok;
46 private $use_class;
47 private $description;
49 function __construct( $success, $description=null, $use_class=null ) {
50 $this->ok = (boolean) $success;
51 $this->description = (isset($description)?$description : ($success===true? i18n('Passed') : i18n('Fail')));
52 $this->use_class = (isset($use_class)?$use_class:($success===true?'dep_ok' : 'dep_fail'));
55 public function getClass() {
56 return $this->use_class;
59 public function setClass( $new_class ) {
60 $this->use_class = $new_class;
63 public function getOK() {
64 return $this->ok;
67 public function getDescription() {
68 return translate($this->description);
71 public function setDescription( $new_desc ) {
72 $this->description = $new_desc;
77 /**
78 * We put many of these checks before we even try to load always.php so that we
79 * can try and do some diagnostic work to ensure it will load OK.
81 function check_pgsql() {
82 return new CheckResult(function_exists('pg_connect'));
85 function check_pdo() {
86 return new CheckResult(class_exists('PDO'));
89 function check_pdo_pgsql() {
90 global $loaded_extensions;
92 if ( !check_pdo() ) return new CheckResult(false);
93 return new CheckResult(isset($loaded_extensions['pdo_pgsql']));
96 function check_database_connection() {
97 global $c;
99 if ( !check_pdo_pgsql() ) return new CheckResult(false);
100 return new CheckResult( $c->schema_major != 0 || $c->schema_minor != 0 );
103 function check_gettext() {
104 global $phpinfo, $loaded_extensions;
106 if ( !function_exists('gettext') ) return new CheckResult(false);
107 return new CheckResult(isset($loaded_extensions['gettext']));
110 function check_iconv() {
111 global $phpinfo, $loaded_extensions;
113 if ( !function_exists('iconv') ) return new CheckResult(false);
114 return new CheckResult(isset($loaded_extensions['iconv']));
117 function check_ldap() {
118 global $phpinfo, $loaded_extensions;
120 if (!function_exists('ldap_connect')) return new CheckResult(false);
121 return new CheckResult(isset($loaded_extensions['ldap']));
124 function check_real_php() {
125 global $phpinfo, $loaded_extensions;
126 // Looking for "Server API </td><td class="v">Apache 2.0 Filter" in the phpinfo
127 if ( preg_match('{Server API.*Apache 2\.. Filter}', $phpinfo) ) return new CheckResult(false);
128 return new CheckResult(true);
131 function check_calendar() {
132 global $phpinfo, $loaded_extensions;
134 if (!function_exists('cal_days_in_month')) return new CheckResult(false);
135 return new CheckResult(isset($loaded_extensions['calendar']));
138 function check_suhosin_server_strip() {
139 global $loaded_extensions;
141 if ( !isset($loaded_extensions['suhosin']) ) return new CheckResult(true);
142 return new CheckResult( ini_get('suhosin.server.strip') == "0" || strtolower(ini_get('suhosin.server.strip')) == "off" );
145 function check_magic_quotes_gpc() {
146 return new CheckResult( (get_magic_quotes_gpc() == 0) );
149 function check_magic_quotes_runtime() {
150 return new CheckResult( (get_magic_quotes_runtime() == 0) );
153 function check_curl() {
154 global $phpinfo, $loaded_extensions;
156 if (!function_exists('curl_init')) return new CheckResult(false);
157 return new CheckResult(isset($loaded_extensions['curl']));
160 $loaded_extensions = array_flip(get_loaded_extensions());
163 function do_error( $errormessage ) {
164 // We can't translate this because we're testing these things even before
165 // the translation interface is available...
166 printf("<p class='error'>%s</p>", $errormessage );
169 if ( !check_gettext()->getOK() ) do_error("The GNU 'gettext' extension for PHP is not available.");
170 if ( !check_pgsql()->getOK() ) do_error("PHP 'pgsql' functions are not available");
171 if ( !check_pdo()->getOK() ) do_error("PHP 'PDO' module is not available");
172 if ( !check_pdo_pgsql()->getOK() ) do_error("The PDO drivers for PostgreSQL are not available");
173 if ( !check_database_connection()->getOK() ) do_error("Unable to connect to database");
174 if ( !check_iconv()->getOK() ) do_error("The 'iconv' extension for PHP is not available");
176 function get_phpinfo() {
177 ob_start( );
178 phpinfo();
179 $phpinfo = ob_get_contents( );
180 ob_end_clean( );
182 $phpinfo = preg_replace( '{^.*?<body>}s', '', $phpinfo);
183 $phpinfo = preg_replace( '{</body>.*?$}s', '', $phpinfo);
184 return $phpinfo;
186 $phpinfo = get_phpinfo();
188 try {
189 include("./always.php");
190 include("DAViCalSession.php");
191 if ( check_pgsql()->GetOK() ) {
192 $session->LoginRequired( (isset($c->restrict_setup_to_admin) && $c->restrict_setup_to_admin ? 'Admin' : null ) );
195 catch( Exception $e ) {
196 set_error_handler('catch_setup_errors', E_ALL);
197 include("DAViCalSession.php");
198 // $session = new FakeSession(1);
202 include("interactive-page.php");
203 include("page-header.php");
205 require_once("AwlQuery.php");
208 function check_datetime() {
209 if ( class_exists('DateTime') ) return new CheckResult(true);
210 $result = new CheckResult(false);
211 $result->setClass('dep_warning');
212 $result->setDescription(i18n('Most of DAViCal will work but upgrading to PHP 5.2 or later is strongly recommended.'));
213 return $result;
216 function check_schema_version() {
217 global $c;
218 if ( $c->want_dbversion[0] == $c->schema_major
219 && $c->want_dbversion[1] == $c->schema_minor
220 && $c->want_dbversion[2] == $c->schema_patch ) {
221 return new CheckResult( true );
223 $result = new CheckResult(false);
224 if ( $c->want_dbversion[0] < $c->schema_major
225 || ($c->want_dbversion[0] == $c->schema_major && $c->want_dbversion[1] < $c->schema_minor)
226 || ($c->want_dbversion[0] == $c->schema_major
227 && $c->want_dbversion[1] == $c->schema_minor
228 && $c->want_dbversion[2] < $c->schema_patch)
231 $result->setClass('dep_warning');
233 $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), implode('.',$c->want_dbversion),
234 $c->schema_major.'.'.$c->schema_minor.'.'.$c->schema_patch));
235 return $result;
238 function check_davical_version() {
239 global $c;
240 $url = 'http://www.davical.org/current_davical_version?v='.$c->version_string;
241 $version_file = @fopen($url, 'r');
242 if ( ! $version_file ) return new CheckResult( false, translate("Could not retrieve") . " '$url'", 'dep_warning' );
243 $current_version = trim(fread( $version_file,12));
244 fclose($version_file);
245 $result = new CheckResult($c->version_string == $current_version);
246 if ( ! $result->getOK() ) {
247 $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), $current_version, $c->version_string) );
248 if ( $c->version_string > $current_version ) $result->setClass('dep_warning');
250 return $result;
254 function check_awl_version() {
255 global $c;
257 if ( !function_exists('awl_version') ) return new CheckResult(false);
259 $result = new CheckResult($c->want_awl_version == awl_version());
260 if ( ! $result->getOK() ) {
261 $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), $c->want_awl_version, awl_version()) );
262 if ( $c->want_awl_version < awl_version() ) $result->setClass('dep_warning');
264 return $result;
269 function build_site_statistics() {
270 $principals = translate('No. of Principals');
271 $collections = translate('No. of Collections');
272 $resources = translate('No. of Resources');
273 $table = <<<EOTABLE
274 <table class="statistics">
275 <tr><th>$principals</th><th>$collections</th><th>$resources</th></tr>
276 <tr>%s</tr>
277 </table>
278 EOTABLE;
280 if ( !check_database_connection() ) {
281 return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>');
283 $sql = 'SELECT
284 (SELECT count(1) FROM principal) AS principals,
285 (SELECT count(1) FROM collection) AS collections,
286 (SELECT count(1) FROM caldav_data) AS resources';
287 $qry = new AwlQuery($sql);
288 if ( $qry->Exec('setup',__LINE__,__FILE__) && $s = $qry->Fetch() ) {
289 $row = sprintf('<td align="center">%s</td><td align="center">%s</td><td align="center">%s</td>',
290 $s->principals, $s->collections, $s->resources );
291 return sprintf( $table, $row );
293 return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>');
297 function build_dependencies_table( ) {
298 global $c;
300 $dependencies = array(
301 translate('Current DAViCal version ') => 'check_davical_version',
302 translate('AWL Library version ') => 'check_awl_version',
303 translate('PHP not using Apache Filter mode') => 'check_real_php',
304 translate('PHP PDO module available') => 'check_pdo',
305 translate('PDO PostgreSQL drivers') => 'check_pdo_pgsql',
306 translate('Database is Connected') => 'check_database_connection',
307 translate('DAViCal DB Schema version ') => 'check_schema_version',
308 translate('GNU gettext support') => 'check_gettext',
309 translate('PHP iconv support') => 'check_iconv',
310 translate('PHP DateTime class') => 'check_datetime',
311 translate('Suhosin "server.strip" disabled') => 'check_suhosin_server_strip',
312 translate('PHP Magic Quotes GPC off') => 'check_magic_quotes_gpc',
313 translate('PHP Magic Quotes runtime off') => 'check_magic_quotes_runtime',
314 translate('PHP calendar extension available') => 'check_calendar',
315 translate('PHP curl support') => 'check_curl'
318 if ( isset($c->authenticate_hook) && isset($c->authenticate_hook['call']) && $c->authenticate_hook['call'] == 'LDAP_check') {
319 $dependencies[translate('PHP LDAP module available')] = 'check_ldap';
322 $dependencies_table = '';
323 $dep_tpl = '<tr class="%s">
324 <td>%s</td>
325 <td>%s</td>
326 <td><a href="http://wiki.davical.org/w/Setup_Failure_Codes/%s">Explanation on DAViCal Wiki</a></td>
327 </tr>
329 foreach( $dependencies AS $k => $v ) {
330 $check_result = $v();
331 $dependencies_table .= sprintf( $dep_tpl, $check_result->getClass(),
333 $check_result->getDescription(),
334 rawurlencode($k)
338 return $dependencies_table;
342 $heading_setup = translate('Setup');
343 $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.');
346 $want_dbversion = implode('.',$c->want_dbversion);
347 $heading_versions = translate('Current Versions');
348 if ( check_schema_version() != true )
350 $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.');
351 $paragraph_versions = sprintf( $paragraph_versions, $c->version_string, $want_dbversion, $c->schema_major, $c->schema_minor, $c->schema_patch);
352 } else {
353 $paragraph_versions = translate('You are currently running DAViCal version %s. The database schema is at version %d.%d.%d.');
354 $paragraph_versions = sprintf( $paragraph_versions, $c->version_string, $c->schema_major, $c->schema_minor, $c->schema_patch);
358 $heading_dependencies = translate('Dependencies');
359 $th_dependency = translate('Dependency');
360 $th_status = translate('Status');
361 $dependencies_table = build_dependencies_table();
363 if ( check_database_connection()->GetOK() ) {
364 $heading_site_statistics = translate('Site Statistics');
365 $site_statistics_table = build_site_statistics();
368 $heading_config_clients = translate('Configuring Calendar Clients for DAViCal');
369 $heading_config_davical = translate('Configuring DAViCal');
370 $davical_configuration_errors = ( $config_warnings == '' ? '' : '<div class="error"><h3 class="error">'
371 . translate('Your configuration produced PHP errors which should be corrected') . '</h3><pre>'
372 . $config_warnings.'</pre></div>'
376 echo <<<EOBODY
377 <style>
378 tr.dep_ok {
379 background-color:#80ff80;
381 tr.dep_fail {
382 background-color:#ff8080;
384 tr.dep_warning {
385 background-color:#ffb040;
387 table, table.dependencies {
388 border: 1px grey solid;
389 border-collapse: collapse;
390 padding: 0.1em;
391 margin: 0 1em 1.5em;
393 table tr td, table tr th, table.dependencies tr td, table.dependencies tr th {
394 border: 1px grey solid;
395 padding: 0.1em 0.2em;
398 padding: 0.3em 0.2em 0.7em;
400 </style>
402 <h1>$heading_setup</h1>
403 <p>$paragraph_setup
405 <h2>$heading_dependencies</h2>
407 <table class="dependencies">
408 <tr>
409 <th>$th_dependency</th>
410 <th>$th_status</th>
411 </tr>
412 $dependencies_table
413 </table>
414 </p>
415 <h2>$heading_config_davical</h2>
416 <p>If you can read this then things must be mostly working already.</p>
417 $davical_configuration_errors
418 <p>The <a href="http://www.davical.org/installation.php">installation page on the DAViCal website</a> has
419 some further information on how to install and configure this application.</p>
421 <h2>$heading_config_clients</h2>
422 <p>The <a href="http://www.davical.org/clients.php">client setup page on the DAViCal website</a> has information on how
423 to configure Evolution, Sunbird, Lightning and Mulberry to use remotely hosted calendars.</p>
424 <p>The administrative interface has no facility for viewing or modifying calendar data.</p>
426 <h2>$heading_site_statistics</h2>
427 <p>$site_statistics_table</p>
429 <h2>PHP Information</h2>
430 <script language="javascript">
431 function toggle_visible() {
432 var argv = toggle_visible.arguments;
433 var argc = argv.length;
435 var fld_checkbox = document.getElementById(argv[0]);
437 if ( argc < 2 ) {
438 return;
441 for (var i = 1; i < argc; i++) {
442 var block_id = argv[i].substr(1);
443 var block_logical = argv[i].substr(0,1);
444 var b = document.getElementById(block_id);
445 if ( block_logical == '!' )
446 b.style.display = (fld_checkbox.checked ? 'none' : '');
447 else
448 b.style.display = (!fld_checkbox.checked ? 'none' : '');
451 </script><p><label>Show phpinfo() output:<input type="checkbox" value="1" id="fld_show_phpinfo" onclick="toggle_visible('fld_show_phpinfo','=phpinfo')"></label></p>
452 <div style="display:none" id="phpinfo">$phpinfo</div>
454 EOBODY;
456 include("page-footer.php");