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