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