1: <?php
2:
3:
4:
5: function i18n($value) {
6: return $value;
7: }
8:
9: $skip_errors = false;
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);
14: }
15:
16: function catch_setup_errors($errno , $errstr , $errfile , $errline , $errcontext ) {
17: if ( $errno == 2 ) {
18:
19: log_setup_error($errno , $errstr , $errfile , $errline);
20: return true;
21: }
22: if ( $errno == 8 ) {
23:
24: log_setup_error($errno , $errstr , $errfile , $errline);
25: return true;
26: }
27: else if ( $errno == 256 ) {
28:
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();
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:
48: set_error_handler('catch_setup_errors', E_ALL);
49:
50: class 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: 85: 86:
87: function check_pgsql() {
88: return new CheckResult(function_exists('pg_connect'));
89: }
90:
91: function check_pdo() {
92: return new CheckResult(class_exists('PDO'));
93: }
94:
95: function 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:
102: function 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:
109: function 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:
116: function 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:
123: function 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:
130: function check_real_php() {
131: global $phpinfo, $loaded_extensions;
132:
133: if ( preg_match('{Server API.*Apache 2\.. Filter}', $phpinfo) ) return new CheckResult(false);
134: return new CheckResult(true);
135: }
136:
137: function 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:
144: function 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:
153: function check_magic_quotes_gpc() {
154: return new CheckResult( (get_magic_quotes_gpc() == 0) );
155: }
156:
157: function check_magic_quotes_runtime() {
158: return new CheckResult( (get_magic_quotes_runtime() == 0) );
159: }
160:
161: function 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:
171: function do_error( $errormessage ) {
172:
173:
174: printf("<p class='error'><br/>%s</p>", $errormessage );
175: }
176:
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_iconv()->getOK() ) do_error("The 'iconv' extension for PHP is not available");
182:
183: function 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:
195: try {
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: }
203: catch( Exception $e ) {
204: class setupFakeSession {
205: function AllowedTo() {
206: return true;
207: }
208: }
209: $session = new setupFakeSession(1);
210: }
211:
212:
213: include("interactive-page.php");
214: include("page-header.php");
215:
216: require_once("AwlQuery.php");
217:
218:
219: function 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:
227: function check_xml() {
228: return new CheckResult(function_exists('xml_parser_create_ns'));
229: }
230:
231: function 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:
253: function 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: }
272: return $result;
273: }
274:
275:
276: function check_awl_version() {
277: global $c;
278:
279: if ( !function_exists('awl_version') ) return new CheckResult(false);
280:
281: $result = new CheckResult($c->want_awl_version == awl_version());
282: if ( ! $result->getOK() ) {
283: $result->setDescription( sprintf(i18n('Want: %s, Currently: %s'), $c->want_awl_version, awl_version()) );
284: if ( $c->want_awl_version < awl_version() ) $result->setClass('dep_warning');
285: }
286: return $result;
287:
288: }
289:
290:
291: function build_site_statistics() {
292: $principals = translate('No. of Principals');
293: $collections = translate('No. of Collections');
294: $resources = translate('No. of Resources');
295: $table = <<<EOTABLE
296: <table class="statistics">
297: <tr><th>$principals</th><th>$collections</th><th>$resources</th></tr>
298: <tr>%s</tr>
299: </table>
300: EOTABLE;
301:
302: if ( !check_database_connection() ) {
303: return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>');
304: }
305: $sql = 'SELECT
306: (SELECT count(1) FROM principal) AS principals,
307: (SELECT count(1) FROM collection) AS collections,
308: (SELECT count(1) FROM caldav_data) AS resources';
309: $qry = new AwlQuery($sql);
310: if ( $qry->Exec('setup',__LINE__,__FILE__) && $s = $qry->Fetch() ) {
311: $row = sprintf('<td align="center">%s</td><td align="center">%s</td><td align="center">%s</td>',
312: $s->principals, $s->collections, $s->resources );
313: return sprintf( $table, $row );
314: }
315: return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>');
316: }
317:
318:
319: function build_dependencies_table( ) {
320: global $c;
321:
322: $dependencies = array(
323: translate('Current DAViCal version ') => 'check_davical_version',
324: translate('AWL Library version ') => 'check_awl_version',
325: translate('PHP not using Apache Filter mode') => 'check_real_php',
326: translate('PHP PDO module available') => 'check_pdo',
327: translate('PDO PostgreSQL drivers') => 'check_pdo_pgsql',
328: translate('Database is Connected') => 'check_database_connection',
329: translate('DAViCal DB Schema version ') => 'check_schema_version',
330: translate('GNU gettext support') => 'check_gettext',
331: translate('PHP iconv support') => 'check_iconv',
332: translate('PHP DateTime class') => 'check_datetime',
333: translate('PHP XML support') => 'check_xml',
334: translate('Suhosin "server.strip" disabled') => 'check_suhosin_server_strip',
335: translate('PHP Magic Quotes GPC off') => 'check_magic_quotes_gpc',
336: translate('PHP Magic Quotes runtime off') => 'check_magic_quotes_runtime',
337: translate('PHP calendar extension available') => 'check_calendar',
338: translate('PHP curl support') => 'check_curl'
339: );
340:
341: if ( isset($c->authenticate_hook) && isset($c->authenticate_hook['call']) && $c->authenticate_hook['call'] == 'LDAP_check') {
342: $dependencies[translate('PHP LDAP module available')] = 'check_ldap';
343: }
344:
345: $translated_failure_code = translate('<a href="https://wiki.davical.org/w/Setup_Failure_Codes/%s">Explanation on DAViCal Wiki</a>');
346:
347: $dependencies_table = '';
348: $dep_tpl = '<tr class="%s">
349: <td>%s</td>
350: <td>%s</td>
351: <td>'.$translated_failure_code.'</td>
352: </tr>
353: ';
354: foreach( $dependencies AS $k => $v ) {
355: $check_result = $v();
356: $dependencies_table .= sprintf( $dep_tpl, $check_result->getClass(),
357: $k,
358: $check_result->getDescription(),
359: rawurlencode($k)
360: );
361: }
362:
363: return $dependencies_table;
364: }
365:
366:
367: $heading_setup = translate('Setup');
368: $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.');
369:
370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381:
382:
383: $heading_dependencies = translate('Dependencies');
384: $th_dependency = translate('Dependency');
385: $th_status = translate('Status');
386: $dependencies_table = build_dependencies_table();
387:
388: $heading_site_statistics = translate('Site Statistics');
389: if ( check_database_connection()->GetOK() ) {
390: try {
391: $site_statistics_table = build_site_statistics();
392: }
393: catch( Exception $e ) {
394: $site_statistics_table = translate('Statistics unavailable');
395: }
396: }
397: else {
398: $site_statistics_table = translate('Statistics unavailable');
399: }
400:
401: $heading_php_info = translate('PHP Information');
402:
403:
404: $heading_clients = translate('Configuring Calendar Clients for DAViCal');
405: $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.');
406: $content_cli2 = translate('The administrative interface has no facility for viewing or modifying calendar data.');
407:
408:
409: $heading_configure = translate('Configuring DAViCal');
410: $content_config1 = translate('If you can read this then things must be mostly working already.');
411: $content_config2 = ( $config_warnings == '' ? '' : '<div class="error"><h3 class="error">'
412: . translate('Your configuration produced PHP errors which should be corrected') . '</h3><pre>'
413: . $config_warnings.'</pre></div>'
414: );
415: $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.');
416:
417:
418: echo <<<EOBODY
419: <style>
420: tr.dep_ok {
421: background-color:#80ff80;
422: }
423: tr.dep_fail {
424: background-color:#ff8080;
425: }
426: tr.dep_warning {
427: background-color:#ffb040;
428: }
429: table, table.dependencies {
430: border: 1px grey solid;
431: border-collapse: collapse;
432: padding: 0.1em;
433: margin: 0 1em 1.5em;
434: }
435: table tr td, table tr th, table.dependencies tr td, table.dependencies tr th {
436: border: 1px grey solid;
437: padding: 0.1em 0.2em;
438: }
439: p {
440: padding: 0.3em 0.2em 0.7em;
441: }
442: </style>
443:
444: <h1>$heading_setup</h1>
445: <p>$paragraph_setup
446:
447: <h2>$heading_dependencies</h2>
448: <p>
449: <table class="dependencies">
450: <tr>
451: <th>$th_dependency</th>
452: <th>$th_status</th>
453: </tr>
454: $dependencies_table
455: </table>
456: </p>
457: <h2>$heading_configure</h2>
458: <p>$content_config1</p>
459: $content_config2
460: <p>$content_config3</p>
461:
462: <h2>$heading_clients</h2>
463: <p>$content_cli1</p>
464: <p>$content_cli2</p>
465:
466: <h2>$heading_site_statistics</h2>
467: <p>$site_statistics_table</p>
468:
469: <h2>$heading_php_info</h2>
470: <script language="javascript">
471: function toggle_visible() {
472: var argv = toggle_visible.arguments;
473: var argc = argv.length;
474:
475: var fld_checkbox = document.getElementById(argv[0]);
476:
477: if ( argc < 2 ) {
478: return;
479: }
480:
481: for (var i = 1; i < argc; i++) {
482: var block_id = argv[i].substr(1);
483: var block_logical = argv[i].substr(0,1);
484: var b = document.getElementById(block_id);
485: if ( block_logical == '!' )
486: b.style.display = (fld_checkbox.checked ? 'none' : '');
487: else
488: b.style.display = (!fld_checkbox.checked ? 'none' : '');
489: }
490: }
491: </script><p><label>Show phpinfo() output:<input type="checkbox" value="1" id="fld_show_phpinfo" onclick="toggle_visible('fld_show_phpinfo','=phpinfo')"></label></p>
492: <div style="display:none" id="phpinfo">$phpinfo</div>
493:
494: EOBODY;
495:
496: include("page-footer.php");
497: