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