Add pointer to doc/ directory
[squirrelmail.git] / src / configtest.php
1 <?php
2
3 /**
4 * SquirrelMail configtest script
5 *
6 * @copyright &copy; 2003-2007 The SquirrelMail Project Team
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
8 * @version $Id$
9 * @package squirrelmail
10 * @subpackage config
11 */
12
13 /************************************************************
14 * NOTE: you do not need to change this script! *
15 * If it throws errors you need to adjust your config. *
16 ************************************************************/
17
18 /** This is the configtest page */
19 define('PAGE_NAME', 'configtest');
20
21 // This script could really use some restructuring as it has grown quite rapidly
22 // but is not very 'clean'. Feel free to get some structure into this thing.
23
24 /** force verbose error reporting and turn on display of errors */
25 error_reporting(E_ALL);
26 ini_set('display_errors',1);
27
28 /** Blockcopy from init.php. Cleans globals. */
29 if ((bool) ini_get('register_globals') &&
30 strtolower(ini_get('register_globals'))!='off') {
31 /**
32 * Remove all globals that are not reserved by PHP
33 * 'value' and 'key' are used by foreach. Don't unset them inside foreach.
34 */
35 foreach ($GLOBALS as $key => $value) {
36 switch($key) {
37 case 'HTTP_POST_VARS':
38 case '_POST':
39 case 'HTTP_GET_VARS':
40 case '_GET':
41 case 'HTTP_COOKIE_VARS':
42 case '_COOKIE':
43 case 'HTTP_SERVER_VARS':
44 case '_SERVER':
45 case 'HTTP_ENV_VARS':
46 case '_ENV':
47 case 'HTTP_POST_FILES':
48 case '_FILES':
49 case '_REQUEST':
50 case 'HTTP_SESSION_VARS':
51 case '_SESSION':
52 case 'GLOBALS':
53 case 'key':
54 case 'value':
55 break;
56 default:
57 unset($GLOBALS[$key]);
58 }
59 }
60 // Unset variables used in foreach
61 unset($GLOBALS['key']);
62 unset($GLOBALS['value']);
63 }
64
65
66 /**
67 * Displays error messages and warnings
68 * @param string $str message
69 * @param boolean $fatal fatal error or only warning
70 */
71 function do_err($str, $fatal = TRUE) {
72 global $IND, $warnings;
73 $level = $fatal ? 'FATAL ERROR:' : 'WARNING:';
74 echo '<p>'.$IND.'<font color="red"><b>' . $level . '</b></font> ' .$str. "</p>\n";
75 if($fatal) {
76 echo '</body></html>';
77 exit;
78 } else {
79 $warnings++;
80 }
81 }
82
83 ob_implicit_flush();
84 /** @ignore */
85 define('SM_PATH', '../');
86 /** load minimal function set */
87 require(SM_PATH . 'include/constants.php');
88 require(SM_PATH . 'functions/global.php');
89 require(SM_PATH . 'functions/strings.php');
90 require(SM_PATH . 'functions/files.php');
91 $SQM_INTERNAL_VERSION = explode('.', SM_VERSION, 3);
92 $SQM_INTERNAL_VERSION[2] = intval($SQM_INTERNAL_VERSION[2]);
93
94 /** set default value in order to block remote access */
95 $allow_remote_configtest=false;
96
97 /** Load all configuration files before output begins */
98
99 /* load default configuration */
100 require(SM_PATH . 'config/config_default.php');
101 /* reset arrays in default configuration */
102 $ldap_server = array();
103 $plugins = array();
104 $fontsets = array();
105 $theme = array();
106 $theme[0]['PATH'] = SM_PATH . 'themes/default_theme.php';
107 $theme[0]['NAME'] = 'Default';
108 $aTemplateSet = array();
109 $aTemplateSet[0]['ID'] = 'default';
110 $aTemplateSet[0]['NAME'] = 'Default';
111 /* load site configuration */
112 if (file_exists(SM_PATH . 'config/config.php')) {
113 require(SM_PATH . 'config/config.php');
114 }
115 /* load local configuration overrides */
116 if (file_exists(SM_PATH . 'config/config_local.php')) {
117 require(SM_PATH . 'config/config_local.php');
118 }
119
120 /**
121 * Include Compatibility plugin if available.
122 */
123 if (!$disable_plugins && file_exists(SM_PATH . 'plugins/compatibility/functions.php'))
124 include_once(SM_PATH . 'plugins/compatibility/functions.php');
125
126 /** Load plugins */
127 global $disable_plugins;
128 $squirrelmail_plugin_hooks = array();
129 if (!$disable_plugins && file_exists(SM_PATH . 'config/plugin_hooks.php')) {
130 require(SM_PATH . 'config/plugin_hooks.php');
131 }
132
133 /** Warning counter */
134 $warnings = 0;
135
136 /** indent */
137 $IND = str_repeat('&nbsp;',4);
138
139 /**
140 * get_location starts session and must be run before output is started.
141 */
142 $test_location = get_location();
143
144 ?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
145 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
146 <html>
147 <head>
148 <meta name="robots" content="noindex,nofollow">
149 <title>SquirrelMail configtest</title>
150 </head>
151 <body>
152 <h1>SquirrelMail configtest</h1>
153
154 <p>This script will try to check some aspects of your SquirrelMail configuration
155 and point you to errors whereever it can find them. You need to go run <tt>conf.pl</tt>
156 in the <tt>config/</tt> directory first before you run this script.</p>
157
158 <?php
159
160 $included = array_map('basename', get_included_files() );
161 if(!in_array('config.php', $included)) {
162 if(!file_exists(SM_PATH . 'config/config.php')) {
163 do_err('Config file '.SM_PATH . 'config/config.php does not exist!<br />'.
164 'You need to run <tt>conf.pl</tt> first.');
165 }
166 do_err('Could not read '.SM_PATH.'config/config.php! Check file permissions.');
167 }
168 if(!in_array('strings.php', $included)) {
169 do_err('Could not include '.SM_PATH.'functions/strings.php!<br />'.
170 'Check permissions on that file.');
171 }
172
173 /* Block remote use of script */
174 if (! $allow_remote_configtest) {
175 sqGetGlobalVar('REMOTE_ADDR',$client_ip,SQ_SERVER);
176 sqGetGlobalVar('SERVER_ADDR',$server_ip,SQ_SERVER);
177
178 if ((! isset($client_ip) || $client_ip!='127.0.0.1') &&
179 (! isset($client_ip) || ! isset($server_ip) || $client_ip!=$server_ip)) {
180 do_err('Enable "Allow remote configtest" option in squirrelmail configuration in order to use this script.');
181 }
182 }
183
184 echo "<p><table>\n<tr><td>SquirrelMail version:</td><td><b>" . SM_VERSION . "</b></td></tr>\n" .
185 '<tr><td>Config file version:</td><td><b>' . $config_version . "</b></td></tr>\n" .
186 '<tr><td>Config file last modified:</td><td><b>' .
187 date ('d F Y H:i:s', filemtime(SM_PATH . 'config/config.php')) .
188 "</b></td></tr>\n</table>\n</p>\n\n";
189
190 /* check $config_version */
191 if ($config_version!='1.5.0') {
192 do_err('Configuration file version does not match required version. Please update your configuration file.');
193 }
194
195
196 /* checking PHP specs */
197
198 echo "Checking PHP configuration...<br />\n";
199
200 if(!check_php_version(4,1,0)) {
201 do_err('Insufficient PHP version: '. PHP_VERSION . '! Minimum required: 4.1.0');
202 }
203
204 echo $IND . 'PHP version ' . PHP_VERSION . ' OK. (You have: ' . phpversion() . ". Minimum: 4.1.0)<br />\n";
205
206 echo $IND . 'display_errors: ' . ini_get('display_errors') . "<br />\n";
207
208 echo $IND . 'error_reporting: ' . ini_get('error_reporting') . "<br />\n";
209
210 $safe_mode = ini_get('safe_mode');
211 if ($safe_mode) {
212 echo $IND . 'safe_mode: ' . $safe_mode;
213 if (empty($prefs_dsn) || empty($addrbook_dsn))
214 echo ' (<font color="red">double check data and attachment directory ownership, etc!</font>)';
215 if (!empty($addrbook_dsn) || !empty($prefs_dsn) || !empty($addrbook_global_dsn))
216 echo ' (<font color="red">does PHP have access to database interface?</font>)';
217 echo "<br />\n";
218 $safe_mode_exec_dir = ini_get('safe_mode_exec_dir');
219 echo $IND . 'safe_mode_exec_dir: ' . $safe_mode_exec_dir . "<br />\n";
220 }
221
222 /* register_globals check: test for boolean false and any string that is not equal to 'off' */
223
224 if ((bool) ini_get('register_globals') &&
225 strtolower(ini_get('register_globals'))!='off') {
226 do_err('You have register_globals turned on. This is not an error, but it CAN be a security hazard. Consider turning register_globals off.', false);
227 }
228
229
230 /* variables_order check */
231
232 // FIXME(?): Hmm, how do we distinguish between when an ini setting is not available (ini_set() returns empty string) and when the administrator set the value to an empty string? The latter is sure to be highly rare, so for now, just assume that empty value means the setting isn't even available (could also check PHP version when this setting was implemented) although, we'll also warn the user if it is empty, with a non-fatal error
233 $variables_order = strtoupper(ini_get('variables_order'));
234 if (empty($variables_order))
235 do_err('Your variables_order setting seems to be empty. Make sure it is undefined in any PHP ini files, .htaccess files, etc. and not specifically set to an empty value or SquirrelMail may not function correctly', false);
236 else if (strpos($variables_order, 'G') === FALSE
237 || strpos($variables_order, 'P') === FALSE
238 || strpos($variables_order, 'C') === FALSE
239 || strpos($variables_order, 'S') === FALSE) {
240 do_err('Your variables_order setting is insufficient for SquirrelMail to function. It needs at least "GPCS", but you have it set to "' . htmlspecialchars($variables_order) . '"', true);
241 } else {
242 echo $IND . "variables_order OK: $variables_order.<br />\n";
243 }
244
245
246 /* gpc_order check (removed from PHP as of v5.0) */
247
248 if (!check_php_version(5)) {
249 // FIXME(?): Hmm, how do we distinguish between when an ini setting is not available (ini_set() returns empty string) and when the administrator set the value to an empty string? The latter is sure to be highly rare, so for now, just assume that empty value means the setting isn't even available (could also check PHP version when this setting was implemented) although, we'll also warn the user if it is empty, with a non-fatal error
250 $gpc_order = strtoupper(ini_get('gpc_order'));
251 if (empty($gpc_order))
252 do_err('Your gpc_order setting seems to be empty. Make sure it is undefined in any PHP ini files, .htaccess files, etc. and not specifically set to an empty value or SquirrelMail may not function correctly', false);
253 else if (strpos($gpc_order, 'G') === FALSE
254 || strpos($gpc_order, 'P') === FALSE
255 || strpos($gpc_order, 'C') === FALSE) {
256 do_err('Your gpc_order setting is insufficient for SquirrelMail to function. It needs to be set to "GPC", but you have it set to "' . htmlspecialchars($gpc_order) . '"', true);
257 } else {
258 echo $IND . "gpc_order OK: $gpc_order.<br />\n";
259 }
260 }
261
262
263 /* check PHP extensions */
264
265 $php_exts = array('session','pcre');
266 $diff = array_diff($php_exts, get_loaded_extensions());
267 if(count($diff)) {
268 do_err('Required PHP extensions missing: '.implode(', ',$diff) );
269 }
270
271 echo $IND . "PHP extensions OK. Dynamic loading is ";
272
273 if (!(bool)ini_get('enable_dl') || (bool)ini_get('safe_mode')) {
274 echo "disabled.<br />\n";
275 } else {
276 echo "enabled.<br />\n";
277 }
278
279
280 /* dangerous php settings */
281 /**
282 * mbstring.func_overload allows to replace original string and regexp functions
283 * with their equivalents from php mbstring extension. It causes problems when
284 * scripts analyze 8bit strings byte after byte or use 8bit strings in regexp tests.
285 * Setting can be controlled in php.ini (php 4.2.0), webserver config (php 4.2.0)
286 * and .htaccess files (php 4.3.5).
287 */
288 if (function_exists('mb_internal_encoding') &&
289 check_php_version(4,2,0) &&
290 (int)ini_get('mbstring.func_overload')!=0) {
291 $mb_error='You have enabled mbstring overloading.'
292 .' It can cause problems with SquirrelMail scripts that rely on single byte string functions.';
293 do_err($mb_error);
294 }
295
296 /**
297 * Do not use SquirrelMail with magic_quotes_* on.
298 */
299 if ( (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime()) ||
300 (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc()) ||
301 ( (bool) ini_get('magic_quotes_sybase') && ini_get('magic_quotes_sybase') != 'off' )
302 ) {
303 $magic_quotes_warning='You have enabled any one of <tt>magic_quotes_runtime</tt>, '
304 .'<tt>magic_quotes_gpc</tt> or <tt>magic_quotes_sybase</tt> in your PHP '
305 .'configuration. We recommend all those settings to be off. SquirrelMail '
306 .'may work with them on, but when experiencing stray backslashes in your mail '
307 .'or other strange behaviour, it may be advisable to turn them off.';
308 do_err($magic_quotes_warning,false);
309 }
310
311 if (ini_get('short_open_tag') == 0) {
312 $short_open_tag_warning = 'You have configured PHP not to allow short tags '
313 . '(<tt>short_open_tag=off</tt>). This shouldn\'t be a problem with '
314 . 'SquirrelMail or any plugin coded coded according to the '
315 . 'SquirrelMail Coding Guidelines, but if you experience problems with '
316 . 'PHP code being displayed in some of the pages and changing setting '
317 . 'to "on" solves the problem, please file a bug report against the '
318 . 'failing plugin. The correct contact information is most likely '
319 . 'to be found in the plugin documentation.';
320 do_err($short_open_tag_warning, false);
321 }
322
323
324 /* check who the web server is running as if possible */
325
326 if ($process_info = get_process_owner_info()) {
327 echo $IND . 'Web server is running as user: ' . $process_info['name'] . ' (' . $process_info['uid'] . ")<br />\n";
328 //echo $IND . 'Web server is running as effective user: ' . $process_info['ename'] . ' (' . $process_info['euid'] . ")<br />\n";
329 echo $IND . 'Web server is running as group: ' . $process_info['group'] . ' (' . $process_info['gid'] . ")<br />\n";
330 //echo $IND . 'Web server is running as effective group: ' . $process_info['egroup'] . ' (' . $process_info['egid'] . ")<br />\n";
331 }
332
333
334 /* checking paths */
335
336 echo "Checking paths...<br />\n";
337
338 if(!file_exists($data_dir)) {
339 // data_dir is not that important in db_setups.
340 if (!empty($prefs_dsn)) {
341 $data_dir_error = "Data dir ($data_dir) does not exist!\n";
342 echo $IND .'<font color="red"><b>ERROR:</b></font> ' . $data_dir_error;
343 } else {
344 do_err("Data dir ($data_dir) does not exist!");
345 }
346 }
347 // don't check if errors
348 if(!isset($data_dir_error) && !is_dir($data_dir)) {
349 if (!empty($prefs_dsn)) {
350 $data_dir_error = "Data dir ($data_dir) is not a directory!\n";
351 echo $IND . '<font color="red"><b>ERROR:</b></font> ' . $data_dir_error;
352 } else {
353 do_err("Data dir ($data_dir) is not a directory!");
354 }
355 }
356 // datadir should be executable - but no clean way to test on that
357 if(!isset($data_dir_error) && !sq_is_writable($data_dir)) {
358 if (!empty($prefs_dsn)) {
359 $data_dir_error = "Data dir ($data_dir) is not writable!\n";
360 echo $IND . '<font color="red"><b>ERROR:</b></font> ' . $data_dir_error;
361 } else {
362 do_err("Data dir ($data_dir) is not writable!");
363 }
364 }
365
366 if (isset($data_dir_error)) {
367 echo " Some plugins might need access to data directory.<br />\n";
368 } else {
369 // todo_ornot: actually write something and read it back.
370 echo $IND . "Data dir OK.<br />\n";
371 }
372
373 if($data_dir == $attachment_dir) {
374 echo $IND . "Attachment dir is the same as data dir.<br />\n";
375 if (isset($data_dir_error)) {
376 do_err($data_dir_error);
377 }
378 } else {
379 if(!file_exists($attachment_dir)) {
380 do_err("Attachment dir ($attachment_dir) does not exist!");
381 }
382 if (!is_dir($attachment_dir)) {
383 do_err("Attachment dir ($attachment_dir) is not a directory!");
384 }
385 if (!sq_is_writable($attachment_dir)) {
386 do_err("I cannot write to attachment dir ($attachment_dir)!");
387 }
388 echo $IND . "Attachment dir OK.<br />\n";
389 }
390
391
392 echo "Checking plugins...<br />\n";
393
394 /* check plugins and themes */
395 //FIXME: check requirements given in plugin _info() function, such as required PHP extensions, Pear packages, other plugins, SM version, etc see development docs for list of returned info from that function
396 //FIXME: update this list with most recent contents of the Obsolete category - I think it has changed recently
397 $bad_plugins = array(
398 'attachment_common', // Integrated into SquirrelMail 1.2 core
399 'auto_prune_sent', // Obsolete: See Proon Automatic Folder Pruning plugin
400 'compose_new_window', // Integrated into SquirrelMail 1.4 core
401 'delete_move_next', // Integrated into SquirrelMail 1.5 core
402 'disk_quota', // Obsolete: See Check Quota plugin
403 'email_priority', // Integrated into SquirrelMail 1.2 core
404 'emoticons', // Obsolete: See HTML Mail plugin
405 'focus_change', // Integrated into SquirrelMail 1.2 core
406 'folder_settings', // Integrated into SquirrelMail 1.5.1 core
407 'global_sql_addressbook', // Integrated into SquirrelMail 1.4 core
408 'hancock', // Not Working: See Random Signature Taglines plugin
409 'msg_flags', // Integrated into SquirrelMail 1.5.1 core
410 'message_source', // Added to SquirrelMail 1.4 Core Plugins (message_details)
411 'motd', // Integrated into SquirrelMail 1.2 core
412 'paginator', // Integrated into SquirrelMail 1.2 core
413 'printer_friendly', // Integrated into SquirrelMail 1.2 core
414 'procfilter', // Obsolete: See Server Side Filter plugin
415 'redhat_php_cgi_fix', // Integrated into SquirrelMail 1.1.1 core
416 'send_to_semicolon', // Integrated into SquirrelMail 1.4.1 core
417 'spamassassin', // Not working beyond SquirrelMail 1.2.7: See Spamassassin SpamFilter (Frontend) v2 plugin
418 'sqcalendar', // Added to SquirrelMail 1.2 Core Plugins (calendar)
419 'sqclock', // Integrated into SquirrelMail 1.2 core
420 'sql_squirrel_logger', // Obsolete: See Squirrel Logger plugin
421 'tmda', // Obsolete: See TMDA Tools plugin
422 'vacation', // Obsolete: See Vacation Local plugin
423 'view_as_html', // Integrated into SquirrelMail 1.5.1 core
424 'xmailer' // Integrated into SquirrelMail 1.2 core
425 );
426
427 if (isset($plugins[0])) {
428 foreach($plugins as $plugin) {
429 if(!file_exists(SM_PATH .'plugins/'.$plugin)) {
430 do_err('You have enabled the <i>'.$plugin.'</i> plugin, but I cannot find it.', FALSE);
431 } elseif (!is_readable(SM_PATH .'plugins/'.$plugin.'/setup.php')) {
432 do_err('You have enabled the <i>'.$plugin.'</i> plugin, but I cannot locate or read its setup.php file.', FALSE);
433 } elseif (in_array($plugin, $bad_plugins)) {
434 do_err('You have enabled the <i>'.$plugin.'</i> plugin, which causes problems with this version of SquirrelMail. Please check the ReleaseNotes or other documentation for more information.', false);
435 }
436 }
437
438
439 // load plugin functions
440 include_once(SM_PATH . 'functions/plugin.php');
441
442 // turn on output buffering in order to prevent output of new lines
443 ob_start();
444 foreach ($plugins as $name) {
445 use_plugin($name);
446
447 // get output and remove whitespace
448 $output = trim(ob_get_contents());
449
450 // if plugin outputs more than newlines and spacing, stop script execution.
451 if (!empty($output)) {
452 $plugin_load_error = 'Some output was produced when plugin <i>' . $name . '</i> was loaded. Usually this means there is an error in the plugin\'s setup or configuration file. The output was: '.htmlspecialchars($output);
453 do_err($plugin_load_error);
454 }
455 }
456 ob_end_clean();
457
458
459 /**
460 * Check the contents of the static plugin hooks array file against
461 * the plugin setup file, which may have changed in an upgrade, etc.
462 * This helps remind admins to re-run the configuration utility when
463 * a plugin has been changed or upgraded.
464 */
465 $static_squirrelmail_plugin_hooks = $squirrelmail_plugin_hooks;
466 $squirrelmail_plugin_hooks = array();
467 foreach ($plugins as $name) {
468 $function = "squirrelmail_plugin_init_$name";
469 if (function_exists($function)) {
470 $function();
471
472 // now iterate through each hook and make sure the
473 // plugin is registered on the correct ones in the
474 // static plugin configuration file
475 //
476 foreach ($squirrelmail_plugin_hooks as $hook_name => $hooked_plugins)
477 foreach ($hooked_plugins as $hooked_plugin => $hooked_function)
478 if ($hooked_plugin == $name
479 && (empty($static_squirrelmail_plugin_hooks[$hook_name][$hooked_plugin])
480 || $static_squirrelmail_plugin_hooks[$hook_name][$hooked_plugin] != $hooked_function))
481 do_err('The plugin <i>' . $name . '</i> is supposed to be registered on the <i>' . $hook_name . '</i> hook, but it is not. You need to re-run the configuration utility and re-save your configuration file.', FALSE);
482 }
483 }
484 $squirrelmail_plugin_hooks = $static_squirrelmail_plugin_hooks;
485
486
487 /**
488 * Print plugin versions
489 */
490 echo $IND . "Plugin versions...<br />\n";
491 foreach ($plugins as $name) {
492 $plugin_version = get_plugin_version($name);
493 $english_name = get_plugin_requirement($name, 'english_name');
494 echo $IND . $IND . (empty($english_name) ? $name . ' ' : $english_name . ' (' . $name . ') ') . (empty($plugin_version) ? '??' : $plugin_version) . "<br />\n";
495
496 // check if this plugin has any other plugin
497 // dependencies and if they are satisfied
498 //
499 $failed_dependencies = check_plugin_dependencies($name);
500 if ($failed_dependencies === SQ_INCOMPATIBLE) {
501 do_err($name . ' is NOT COMPATIBLE with this version of SquirrelMail', FALSE);
502 }
503 else if (is_array($failed_dependencies)) {
504 $missing_plugins = '';
505 $incompatible_plugins = '';
506 foreach ($failed_dependencies as $depend_name => $depend_requirements) {
507 if ($depend_requirements['version'] == SQ_INCOMPATIBLE)
508 $incompatible_plugins .= ', ' . $depend_name;
509 else
510 $missing_plugins .= ', ' . $depend_name . ' (version ' . $depend_requirements['version'] . ', ' . ($depend_requirements['activate'] ? 'must be activated' : 'need not be activated') . ')';
511 }
512 $error_string = (!empty($incompatible_plugins) ? $name . ' cannot be activated at the same time as the following plugins: ' . trim($incompatible_plugins, ', ') : '')
513 . (!empty($missing_plugins) ? (!empty($incompatible_plugins) ? '. ' . $name . ' is also ' : $name . ' is ') . 'missing some dependencies: ' . trim($missing_plugins, ', ') : '');
514 do_err($error_string, FALSE);
515 }
516
517 }
518
519
520 /**
521 * This hook was added in 1.5.2 and 1.4.10. Each plugins should print an error
522 * message and return TRUE if there are any errors in its setup/configuration.
523 */
524 $plugin_err = boolean_hook_function('configtest', $null, 1);
525 if($plugin_err) {
526 do_err('Some plugin tests failed.');
527 } else {
528 echo $IND . "Plugins OK.<br />\n";
529 }
530 } else {
531 echo $IND . "Plugins are not enabled in config.<br />\n";
532 }
533 foreach($theme as $thm) {
534 if(!file_exists($thm['PATH'])) {
535 do_err('You have enabled the <i>'.$thm['NAME'].'</i> theme but I cannot find it ('.$thm['PATH'].').', FALSE);
536 } elseif(!is_readable($thm['PATH'])) {
537 do_err('You have enabled the <i>'.$thm['NAME'].'</i> theme but I cannot read it ('.$thm['PATH'].').', FALSE);
538 }
539 }
540
541 echo $IND . "Themes OK.<br />\n";
542
543 if ( $squirrelmail_default_language != 'en_US' ) {
544 $loc_path = SM_PATH .'locale/'.$squirrelmail_default_language.'/LC_MESSAGES/squirrelmail.mo';
545 if( ! file_exists( $loc_path ) ) {
546 do_err('You have set <i>' . $squirrelmail_default_language .
547 '</i> as your default language, but I cannot find this translation (should be '.
548 'in <tt>' . $loc_path . '</tt>). Please note that you have to download translations '.
549 'separately from the main SquirrelMail package.', FALSE);
550 } elseif ( ! is_readable( $loc_path ) ) {
551 do_err('You have set <i>' . $squirrelmail_default_language .
552 '</i> as your default language, but I cannot read this translation (file '.
553 'in <tt>' . $loc_path . '</tt> unreadable).', FALSE);
554 } else {
555 echo $IND . "Default language OK.<br />\n";
556 }
557 } else {
558 echo $IND . "Default language OK.<br />\n";
559 }
560
561 echo $IND . "Base URL detected as: <tt>" . htmlspecialchars($test_location) .
562 "</tt> (location base " . (empty($config_location_base) ? 'autodetected' : 'set to <tt>' .
563 htmlspecialchars($config_location_base)."</tt>") . ")<br />\n";
564
565 /* check minimal requirements for other security options */
566
567 /* imaps or ssmtp */
568 if($use_smtp_tls == 1 || $use_imap_tls == 1) {
569 if(!check_php_version(4,3,0)) {
570 do_err('You need at least PHP 4.3.0 for SMTP/IMAP TLS!');
571 }
572 if(!extension_loaded('openssl')) {
573 do_err('You need the openssl PHP extension to use SMTP/IMAP TLS!');
574 }
575 }
576 /* starttls extensions */
577 if($use_smtp_tls === 2 || $use_imap_tls === 2) {
578 if (! function_exists('stream_socket_enable_crypto')) {
579 do_err('If you want to use STARTTLS extension, you need stream_socket_enable_crypto() function from PHP 5.1.0 and newer.');
580 }
581 }
582 /* digest-md5 */
583 if ($smtp_auth_mech=='digest-md5' || $imap_auth_mech =='digest-md5') {
584 if (!extension_loaded('xml')) {
585 do_err('You need the PHP XML extension to use Digest-MD5 authentication!');
586 }
587 }
588
589 /* check outgoing mail */
590
591 echo "Checking outgoing mail service....<br />\n";
592
593 if($useSendmail) {
594 // is_executable also checks for existance, but we want to be as precise as possible with the errors
595 if(!file_exists($sendmail_path)) {
596 do_err("Location of sendmail program incorrect ($sendmail_path)!");
597 }
598 if(!is_executable($sendmail_path)) {
599 do_err("I cannot execute the sendmail program ($sendmail_path)!");
600 }
601
602 echo $IND . "sendmail OK<br />\n";
603 } else {
604 $stream = fsockopen( ($use_smtp_tls==1?'tls://':'').$smtpServerAddress, $smtpPort,
605 $errorNumber, $errorString);
606 if(!$stream) {
607 do_err("Error connecting to SMTP server \"$smtpServerAddress:$smtpPort\".".
608 "Server error: ($errorNumber) ".htmlspecialchars($errorString));
609 }
610
611 // check for SMTP code; should be 2xx to allow us access
612 $smtpline = fgets($stream, 1024);
613 if(((int) $smtpline{0}) > 3) {
614 do_err("Error connecting to SMTP server. Server error: ".
615 htmlspecialchars($smtpline));
616 }
617
618 /* smtp starttls checks */
619 if ($use_smtp_tls===2) {
620 // if something breaks, script should close smtp connection on exit.
621
622 // say helo
623 fwrite($stream,"EHLO $client_ip\r\n");
624
625 $ehlo=array();
626 $ehlo_error = false;
627 while ($line=fgets($stream, 1024)){
628 if (preg_match("/^250(-|\s)(\S*)\s+(\S.*)/",$line,$match)||
629 preg_match("/^250(-|\s)(\S*)\s+/",$line,$match)) {
630 if (!isset($match[3])) {
631 // simple one word extension
632 $ehlo[strtoupper($match[2])]='';
633 } else {
634 // ehlo-keyword + ehlo-param
635 $ehlo[strtoupper($match[2])]=trim($match[3]);
636 }
637 if ($match[1]==' ') {
638 $ret = $line;
639 break;
640 }
641 } else {
642 //
643 $ehlo_error = true;
644 $ehlo[]=$line;
645 break;
646 }
647 }
648 if ($ehlo_error) {
649 do_err('SMTP EHLO failed. You need ESMTP support for SMTP STARTTLS');
650 } elseif (!array_key_exists('STARTTLS',$ehlo)) {
651 do_err('STARTTLS support is not declared by SMTP server.');
652 }
653
654 fwrite($stream,"STARTTLS\r\n");
655 $starttls_response=fgets($stream, 1024);
656 if ($starttls_response[0]!=2) {
657 $starttls_cmd_err = 'SMTP STARTTLS failed. Server replied: '
658 .htmlspecialchars($starttls_response);
659 do_err($starttls_cmd_err);
660 } elseif(! stream_socket_enable_crypto($stream,true,STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
661 do_err('Failed to enable encryption on SMTP STARTTLS connection.');
662 } else {
663 echo $IND . "SMTP STARTTLS extension looks OK.<br />\n";
664 }
665 // According to RFC we should second ehlo call here.
666 }
667
668 fputs($stream, 'QUIT');
669 fclose($stream);
670 echo $IND . 'SMTP server OK (<tt><small>'.
671 trim(htmlspecialchars($smtpline))."</small></tt>)<br />\n";
672
673 /* POP before SMTP */
674 if($pop_before_smtp) {
675 if (empty($pop_before_smtp_host)) $pop_before_smtp_host = $smtpServerAddress;
676 $stream = fsockopen($pop_before_smtp_host, 110, $err_no, $err_str);
677 if (!$stream) {
678 do_err("Error connecting to POP Server ($pop_before_smtp_host:110) "
679 . $err_no . ' : ' . htmlspecialchars($err_str));
680 }
681
682 $tmp = fgets($stream, 1024);
683 if (substr($tmp, 0, 3) != '+OK') {
684 do_err("Error connecting to POP Server ($pop_before_smtp_host:110)"
685 . ' '.htmlspecialchars($tmp));
686 }
687 fputs($stream, 'QUIT');
688 fclose($stream);
689 echo $IND . "POP-before-SMTP OK.<br />\n";
690 }
691 }
692
693 /**
694 * Check the IMAP server
695 */
696 echo "Checking IMAP service....<br />\n";
697
698 /** Can we open a connection? */
699 $stream = fsockopen( ($use_imap_tls==1?'tls://':'').$imapServerAddress, $imapPort,
700 $errorNumber, $errorString);
701 if(!$stream) {
702 do_err("Error connecting to IMAP server \"$imapServerAddress:$imapPort\".".
703 "Server error: ($errorNumber) ".
704 htmlspecialchars($errorString));
705 }
706
707 /** Is the first response 'OK'? */
708 $imapline = fgets($stream, 1024);
709 if(substr($imapline, 0,4) != '* OK') {
710 do_err('Error connecting to IMAP server. Server error: '.
711 htmlspecialchars($imapline));
712 }
713
714 echo $IND . 'IMAP server ready (<tt><small>'.
715 htmlspecialchars(trim($imapline))."</small></tt>)<br />\n";
716
717 /** Check capabilities */
718 fputs($stream, "A001 CAPABILITY\r\n");
719 $capline = '';
720 while ($line=fgets($stream, 1024)){
721 if (preg_match("/A001.*/",$line)) {
722 break;
723 } else {
724 $capline.=$line;
725 }
726 }
727
728 /* don't display capabilities before STARTTLS */
729 if ($use_imap_tls===2 && stristr($capline, 'STARTTLS') === false) {
730 do_err('Your server doesn\'t support STARTTLS.');
731 } elseif($use_imap_tls===2) {
732 /* try starting starttls */
733 fwrite($stream,"A002 STARTTLS\r\n");
734 $starttls_line=fgets($stream, 1024);
735 if (! preg_match("/^A002 OK.*/i",$starttls_line)) {
736 $imap_starttls_err = 'IMAP STARTTLS failed. Server replied: '
737 .htmlspecialchars($starttls_line);
738 do_err($imap_starttls_err);
739 } elseif (! stream_socket_enable_crypto($stream,true,STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
740 do_err('Failed to enable encryption on IMAP connection.');
741 } else {
742 echo $IND . "IMAP STARTTLS extension looks OK.<br />\n";
743 }
744
745 // get new capability line
746 fwrite($stream,"A003 CAPABILITY\r\n");
747 $capline='';
748 while ($line=fgets($stream, 1024)){
749 if (preg_match("/A003.*/",$line)) {
750 break;
751 } else {
752 $capline.=$line;
753 }
754 }
755 }
756
757 echo $IND . 'Capabilities: <tt>'.htmlspecialchars($capline)."</tt><br />\n";
758
759 if($imap_auth_mech == 'login' && stristr($capline, 'LOGINDISABLED') !== FALSE) {
760 do_err('Your server doesn\'t allow plaintext logins. '.
761 'Try enabling another authentication mechanism like CRAM-MD5, DIGEST-MD5 or TLS-encryption '.
762 'in the SquirrelMail configuration.', FALSE);
763 }
764
765 if (stristr($capline, 'XMAGICTRASH') !== false) {
766 $magic_trash = 'It looks like IMAP_MOVE_EXPUNGE_TO_TRASH option is turned on '
767 .'in your Courier IMAP configuration. Courier does not provide tools that '
768 .'allow to detect folder used for Trash or commands are not documented. '
769 .'SquirrelMail can\'t detect special trash folder. SquirrelMail manages '
770 .'all message deletion or move operations internally and '
771 .'IMAP_MOVE_EXPUNGE_TO_TRASH option can cause errors in message and '
772 .'folder management operations. Please turn off IMAP_MOVE_EXPUNGE_TO_TRASH '
773 .'option in Courier imapd configuration.';
774 do_err($magic_trash,false);
775 }
776
777 /* add warning about IMAP delivery */
778 if (stristr($capline, 'XCOURIEROUTBOX') !== false) {
779 $courier_outbox = 'OUTBOX setting is enabled in your Courier imapd '
780 .'configuration. SquirrelMail uses standard SMTP protocol or sendmail '
781 .'binary to send emails. Courier IMAP delivery method is not supported'
782 .' and can create duplicate email messages.';
783 do_err($courier_outbox,false);
784 }
785
786 /** OK, close connection */
787 fputs($stream, "A004 LOGOUT\r\n");
788 fclose($stream);
789
790 echo "Checking internationalization (i18n) settings...<br />\n";
791 echo "$IND gettext - ";
792 if (function_exists('gettext')) {
793 echo 'Gettext functions are available.'
794 .' On some systems you must have appropriate system locales compiled.'
795 ."<br />\n";
796
797 /* optional setlocale() tests. Should work only on glibc systems. */
798 if (sqgetGlobalVar('testlocales',$testlocales,SQ_GET)) {
799 include_once(SM_PATH . 'include/languages.php');
800 echo $IND . $IND . 'Testing translations:<br>';
801 foreach ($languages as $lang_code => $lang_data) {
802 /* don't test aliases */
803 if (isset($lang_data['NAME'])) {
804 /* locale can be $lang_code or $lang_data['LOCALE'] */
805 if (isset($lang_data['LOCALE'])) {
806 $setlocale = $lang_data['LOCALE'];
807 } else {
808 $setlocale = $lang_code;
809 }
810 /* prepare information about tested locales */
811 if (is_array($setlocale)) {
812 $display_locale = implode(', ',$setlocale);
813 $locale_count = count($setlocale);
814 } else {
815 $display_locale = $setlocale;
816 $locale_count = 1;
817 }
818 $tested_locales_msg = 'Tested '.htmlspecialchars($display_locale).' '
819 .($locale_count>1 ? 'locales':'locale'). '.';
820
821 echo $IND . $IND .$IND . $lang_data['NAME'].' (' .$lang_code. ') - ';
822 $retlocale = sq_setlocale(LC_ALL,$setlocale);
823 if (is_bool($retlocale)) {
824 echo '<font color="red">unsupported</font>. ';
825 echo $tested_locales_msg;
826 } else {
827 echo 'supported. '
828 .$tested_locales_msg
829 .' setlocale() returned "'.htmlspecialchars($retlocale).'"';
830 }
831 echo "<br />\n";
832 }
833 }
834 echo $IND . $IND . '<a href="configtest.php">Don\'t test translations</a>';
835 } else {
836 echo $IND . $IND . '<a href="configtest.php?testlocales=1">Test translations</a>. '
837 .'This test is not accurate and might work only on some systems.'
838 ."\n";
839 }
840 echo "<br />\n";
841 /* end of translation tests */
842 } else {
843 echo 'Gettext functions are unavailable.'
844 .' SquirrelMail will use slower internal gettext functions.'
845 ."<br />\n";
846 }
847 echo "$IND mbstring - ";
848 if (function_exists('mb_detect_encoding')) {
849 echo "Mbstring functions are available.<br />\n";
850 } else {
851 echo 'Mbstring functions are unavailable.'
852 ." Japanese translation won't work.<br />\n";
853 }
854 echo "$IND recode - ";
855 if (function_exists('recode')) {
856 echo "Recode functions are available.<br />\n";
857 } elseif (isset($use_php_recode) && $use_php_recode) {
858 echo "Recode functions are unavailable.<br />\n";
859 do_err('Your configuration requires recode support, but recode support is missing.');
860 } else {
861 echo "Recode functions are unavailable.<br />\n";
862 }
863 echo "$IND iconv - ";
864 if (function_exists('iconv')) {
865 echo "Iconv functions are available.<br />\n";
866 } elseif (isset($use_php_iconv) && $use_php_iconv) {
867 echo "Iconv functions are unavailable.<br />\n";
868 do_err('Your configuration requires iconv support, but iconv support is missing.');
869 } else {
870 echo "Iconv functions are unavailable.<br />\n";
871 }
872 // same test as in include/init.php + date_default_timezone_set check
873 echo "$IND timezone - ";
874 if ( (!ini_get('safe_mode')) || function_exists('date_default_timezone_set') ||
875 !strcmp(ini_get('safe_mode_allowed_env_vars'),'') ||
876 preg_match('/^([\w_]+,)*TZ/', ini_get('safe_mode_allowed_env_vars')) ) {
877 echo "Webmail users can change their time zone settings. \n";
878 } else {
879 echo "Webmail users can't change their time zone settings. \n";
880 }
881 if (isset($_ENV['TZ'])) {
882 echo 'Default time zone is '.htmlspecialchars($_ENV['TZ']);
883 } else {
884 echo 'Current time zone is '.date('T');
885 }
886 echo ".<br />\n";
887
888 // Pear DB tests
889 echo "Checking database functions...<br />\n";
890 if($addrbook_dsn || $prefs_dsn || $addrbook_global_dsn) {
891 @include_once('DB.php');
892 if (class_exists('DB')) {
893 echo "$IND PHP Pear DB support is present.<br />\n";
894 $db_functions=array(
895 'dbase' => 'dbase_open',
896 'fbsql' => 'fbsql_connect',
897 'interbase' => 'ibase_connect',
898 'informix' => 'ifx_connect',
899 'msql' => 'msql_connect',
900 'mssql' => 'mssql_connect',
901 'mysql' => 'mysql_connect',
902 'mysqli' => 'mysqli_connect',
903 'oci8' => 'ocilogon',
904 'odbc' => 'odbc_connect',
905 'pgsql' => 'pg_connect',
906 'sqlite' => 'sqlite_open',
907 'sybase' => 'sybase_connect'
908 );
909
910 $dsns = array();
911 if($prefs_dsn) {
912 $dsns['preferences'] = $prefs_dsn;
913 }
914 if($addrbook_dsn) {
915 $dsns['addressbook'] = $addrbook_dsn;
916 }
917 if($addrbook_global_dsn) {
918 $dsns['global addressbook'] = $addrbook_global_dsn;
919 }
920
921 foreach($dsns as $type => $dsn) {
922 $aDsn = explode(':', $dsn);
923 $dbtype = array_shift($aDsn);
924
925 if(isset($db_functions[$dbtype]) && function_exists($db_functions[$dbtype])) {
926 echo "$IND$dbtype database support present.<br />\n";
927 } elseif(!(bool)ini_get('enable_dl') || (bool)ini_get('safe_mode')) {
928 do_err($dbtype.' database support not present!');
929 } else {
930 // Non-fatal error
931 do_err($dbtype.' database support not present or not configured!
932 Trying to dynamically load '.$dbtype.' extension.
933 Please note that it is advisable to not rely on dynamic loading of extensions.', FALSE);
934 }
935
936
937 // now, test this interface:
938
939 $dbh = DB::connect($dsn, true);
940 if (DB::isError($dbh)) {
941 do_err('Database error: '. htmlspecialchars(DB::errorMessage($dbh)) .
942 ' in ' .$type .' DSN.');
943 }
944 $dbh->disconnect();
945 echo "$IND$type database connect successful.<br />\n";
946 }
947 } else {
948 $db_error='Required PHP PEAR DB support is not available.'
949 .' Is PEAR installed and is the include path set correctly to find <tt>DB.php</tt>?'
950 .' The include path is now: "<tt>' . ini_get('include_path') . '</tt>".';
951 do_err($db_error);
952 }
953 } else {
954 echo $IND."not using database functionality.<br />\n";
955 }
956
957 // LDAP DB tests
958 echo "Checking LDAP functions...<br />\n";
959 if( empty($ldap_server) ) {
960 echo $IND."not using LDAP functionality.<br />\n";
961 } else {
962 if ( !function_exists('ldap_connect') ) {
963 do_err('Required LDAP support is not available.');
964 } else {
965 echo "$IND LDAP support present.<br />\n";
966 foreach ( $ldap_server as $param ) {
967
968 $linkid = @ldap_connect($param['host'], (empty($param['port']) ? 389 : $param['port']) );
969
970 if ( $linkid ) {
971 echo "$IND LDAP connect to ".$param['host']." successful: ".$linkid."<br />\n";
972
973 if ( !empty($param['protocol']) &&
974 !ldap_set_option($linkid, LDAP_OPT_PROTOCOL_VERSION, $param['protocol']) ) {
975 do_err('Unable to set LDAP protocol');
976 }
977
978 if ( empty($param['binddn']) ) {
979 $bind = @ldap_bind($linkid);
980 } else {
981 $bind = @ldap_bind($linkid, $param['binddn'], $param['bindpw']);
982 }
983
984 if ( $bind ) {
985 echo "$IND LDAP Bind Successful <br />";
986 } else {
987 do_err('Unable to Bind to LDAP Server');
988 }
989
990 @ldap_close($linkid);
991 } else {
992 do_err('Connection to LDAP failed');
993 }
994 }
995 }
996 }
997
998 echo '<hr width="75%" align="center">';
999 echo '<h2 align="center">Summary</h2>';
1000 $footer = '<hr width="75%" align="center">';
1001 if ($warnings) {
1002 echo '<p>No fatal errors were found, but there was at least 1 warning. Please check the flagged issue(s) carefully, as correcting them may prevent erratic, undefined, or incorrect behavior (or flat out breakage).</p>';
1003 echo $footer;
1004 } else {
1005 print <<< EOF
1006 <p>Congratulations, your SquirrelMail setup looks fine to me!</p>
1007
1008 <p><a href="login.php">Login now</a></p>
1009
1010 </body>
1011 </html>
1012 EOF;
1013 echo $footer;
1014 }