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