4
* Define HORDE_BASE, if it is not already set, and include the main
5
* Horde library, since we require it for this package to function.
7
if (!defined('HORDE_BASE')) {
8
define('HORDE_BASE', dirname(__FILE__) . '/..');
10
require_once HORDE_BASE . '/lib/core.php';
13
* Set the path to the templates needed for testing output.
15
define('TEST_TEMPLATES', HORDE_BASE . '/templates/test/');
17
/* If gettext is not loaded, define a dummy _() function so that
18
* including any file with gettext strings won't cause a fatal error,
19
* causing test.php to return a blank page. */
20
if (!function_exists('_')) {
21
function _($s) { return $s; }
25
* The Horde_Test:: class provides functions used in the test scripts
26
* used in the various applications (test.php).
28
* $Horde: horde/lib/Test.php,v 1.31.4.4 2005/03/05 20:21:15 chuck Exp $
30
* Copyright 1999-2005 Charles J. Hagenbuch <chuck@horde.org>
31
* Copyright 1999-2005 Jon Parise <jon@horde.org>
32
* Copyright 2002-2005 Brent J. Nordquist <bjn@horde.org>
33
* Copyright 2003-2005 Michael Slusarz <slusarz@bigworm.colorado.edu>
35
* See the enclosed file COPYING for license information (LGPL). If you
36
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
38
* @author Chuck Hagenbuch <chuck@horde.org>
39
* @author Jon Parise <jon@horde.org>
40
* @author Brent J. Nordquist <bjn@horde.org>
41
* @author Michael Slusarz <slusarz@bigworm.colorado.edu>
48
* Array that holds the list of Horde applications.
49
* (Loaded from config/registry.php)
51
* @var array $applications
53
var $applications = array();
56
* Cached results of getApplications().
58
* @var array $_appoutput
60
var $_appoutput = array();
63
* The PHP version of the system.
70
* Location of the base Horde test script.
72
* @var string $_testscript
83
include_once HORDE_BASE . '/config/registry.php';
84
ksort($this->applications);
86
/* Set the location of the base test script. */
87
$this->_testscript = $_SERVER['PHP_SELF'];
89
/* Store the PHP version information. */
90
$this->_phpver = $this->splitPHPVersion(phpversion());
92
/* We want to be as verbose as possible here. */
93
error_reporting(E_ALL);
95
/* Set character encoding. */
96
header('Content-type: text/html; charset=utf-8');
97
header('Vary: Accept-Language');
105
* @param string $version A PHP-style version string (X.X.X).
107
* @param array The parsed string.
108
* Keys: 'major', 'minor', 'subminor', 'class'
110
function splitPHPVersion($version)
112
/* First pick off major version, and lower-case the rest. */
113
if ((strlen($version) >= 3) && ($version[1] == '.')) {
114
$phpver['major'] = substr($version, 0, 3);
115
$version = substr(strtolower($version), 3);
117
$phpver['major'] = $version;
118
$phpver['class'] = 'unknown';
122
if ($version[0] == '.') {
123
$version = substr($version, 1);
126
/* Next, determine if this is 4.0b or 4.0rc; if so, there is no
127
minor, the rest is the subminor, and class is set to beta. */
128
$s = strspn($version, '0123456789');
130
$phpver['subminor'] = $version;
131
$phpver['class'] = 'beta';
135
/* Otherwise, this is non-beta; the numeric part is the minor,
136
the rest is either a classification (dev, cvs) or a subminor
137
version (rc<x>, pl<x>). */
138
$phpver['minor'] = substr($version, 0, $s);
139
if ((strlen($version) > $s) &&
140
(($version[$s] == '.') || ($version[$s] == '-'))) {
143
$phpver['subminor'] = substr($version, $s);
144
if (($phpver['subminor'] == 'cvs') ||
145
($phpver['subminor'] == 'dev') ||
146
(substr($phpver['subminor'], 0, 2) == 'rc')) {
147
unset($phpver['subminor']);
148
$phpver['class'] = 'dev';
150
if (!$phpver['subminor']) {
151
unset($phpver['subminor']);
153
$phpver['class'] = 'release';
160
* Check the list of PHP modules.
164
* @param array $modlist The module list.
167
* VALUE: Either the description or an array with the following entries:
168
* 'descrip' -- Module Description
169
* 'error' -- Error Message
170
* 'phpver' -- The PHP version above which to do the test
173
* @return string The HTML output.
175
function phpModuleCheck($modlist)
178
$output_array = array();
180
foreach ($modlist as $key => $val) {
181
$error_msg = $mod_test = $status_out = $fatal = null;
184
if (is_array($val)) {
185
$descrip = $val['descrip'];
186
$fatal = !empty($val['fatal']);
187
if (isset($val['phpver']) &&
188
(version_compare(phpversion(), $val['phpver']) == -1)) {
192
if (isset($val['error'])) {
193
$error_msg = $val['error'];
199
if (is_null($status_out)) {
200
$mod_test = extension_loaded($key);
201
$status_out = $this->_status($mod_test, $fatal);
205
$entry[] = $status_out;
207
if (!is_null($error_msg) && !$mod_test) {
208
$entry[] = $error_msg;
214
$output .= $this->_outputLine($entry);
216
if ($fatal && !$mod_test) {
226
* Checks the list of PHP settings.
230
* @param array $modlist The settings list.
233
* VALUE: An array with the following entries:
234
* 'error' -- Error Message
235
* 'setting' -- Boolean - should the setting be on or off
238
* @return string The HTML output.
240
function phpSettingCheck($settings_list)
244
foreach ($settings_list as $key => $val) {
246
$result = (ini_get($key) == $val['setting']);
248
$entry[] = $key . ' ' . (($val['setting'] === true) ? 'enabled' : 'disabled');
249
$entry[] = $this->_status($result);
252
$entry[] = $val['error'];
255
$output .= $this->_outputLine($entry);
262
* Check the list of PEAR modules.
266
* @param array $pear_list The PEAR module list.
268
* KEY: PEAR class name
269
* VALUE: An array with the following entries:
270
* 'depends' -- This module depends on another module
271
* 'error' -- Error Message
272
* 'function' -- Reference to function to run if module is found
273
* 'path' -- The path to the PEAR module
274
* 'required' -- Is this PEAR module required? (boolean)
277
* @return string The HTML output.
279
function PEARModuleCheck($pear_list)
283
/* Turn tracking of errors on. */
284
ini_set('track_errors', 1);
286
/* Print the include_path. */
287
$output .= $this->_outputLine(array("<b>PEAR Search Path (PHP's include_path)</b>", ' <tt>' . ini_get('include_path') . '</tt>'));
289
/* Check for PEAR in general. */
293
@include_once 'PEAR.php';
294
$entry[] = $this->_status(!isset($php_errormsg));
295
if (isset($php_errormsg)) {
296
$entry[] = 'Check your PHP include_path setting to make sure it has the PEAR library directory.';
297
$output .= $this->_outputLine($entry);
298
ini_restore('track_errors');
301
$output .= $this->_outputLine($entry);
304
/* Check for a recent PEAR version. */
306
$newpear = $this->isRecentPEAR();
307
$entry[] = 'Recent PEAR';
308
$entry[] = $this->_status($newpear);
310
$entry[] = 'This version of PEAR is not recent enough. See the <a href="http://www.horde.org/pear/">Horde PEAR page</a> for details.';
312
$output .= $this->_outputLine($entry);
314
/* Go through module list. */
315
$succeeded = array();
316
foreach ($pear_list as $key => $val) {
319
/* If this module depends on another module that we
320
* haven't succesfully found, fail the test. */
321
if (!empty($val['depends']) && empty($succeeded[$val['depends']])) {
324
$result = @include_once $val['path'];
326
$error_msg = $val['error'];
327
if ($result && isset($val['function'])) {
328
$func_output = call_user_func($val['function']);
331
$error_msg = $func_output;
335
$entry[] = $this->_status($result, !empty($val['required']));
338
$succeeded[$key] = true;
340
if (!empty($val['required'])) {
341
$error_msg .= ' THIS IS A REQUIRED MODULE!';
343
$entry[] = $error_msg;
344
if (empty($val['required'])) {
349
$output .= $this->_outputLine($entry);
352
/* Restore previous value of 'track_errors'. */
353
ini_restore('track_errors');
359
* Check the list of required files
363
* @param array $file_list The file list.
366
* VALUE: The error message to use (null to use default message)
369
* @return string The HTML output.
371
function requiredFileCheck($file_list)
375
foreach ($file_list as $key => $val) {
377
$result = file_exists('./' . $key);
380
$entry[] = $this->_status($result);
384
$entry[] = 'The file <code>' . $key . '</code> appears to be missing. You probably just forgot to copy <code>' . $key . '.dist</code> over. While you do that, take a look at the settings and make sure they are appropriate for your site.';
390
$output .= $this->_outputLine($entry);
397
* Displays an error screen with a list of all configuration files
398
* that are missing, together with a description what they do and
399
* how they are created. If a file can be automatically created
400
* from the defaults, then we do that instead and don't display an
403
* @param string $app The application name
404
* @param string $appBase The path to the application
405
* @param array $files An array with the "standard" configuration files
406
* that should be checked. Currently supported:
410
* @param array $additional (optional) An associative array containing
411
* more files (as keys) and error message (as
412
* values) if they don't exist.
414
function configFilesMissing($app, $appBase, $files, $additional = array())
416
if (!is_array($files)) {
417
$files = array($files);
419
$files = array_merge($files, array_keys($additional));
421
/* Try to auto-create missing .dist files. */
422
$indices = array_keys($files);
423
foreach ($indices as $index) {
424
if (is_readable($appBase . '/config/' . $files[$index])) {
425
unset($files[$index]);
427
if (@file_exists($appBase . '/config/' . $files[$index] . '.dist') &&
428
@copy($appBase . '/config/' . $files[$index] . '.dist', $appBase . '/config/' . $files[$index])) {
429
unset($files[$index]);
434
/* Return if we have no missing files left. */
435
if (!count($files)) {
439
$descriptions = array_merge(array(
440
'conf.php' => sprintf('This is the main %s configuration file. ' .
441
'It contains paths and options for the %s ' .
442
'scripts. You need to login as an ' .
443
'administrator and create the file with ' .
444
'the web frontend under "Administration => ' .
446
$app, $app, $appBase . '/config'),
447
'prefs.php' => sprintf('This file controls the default preferences ' .
448
'for %s, and also controls which preferences ' .
449
'users can alter.', $app),
450
'mime_drivers.php' => sprintf('This file controls local MIME ' .
451
'drivers for %s, specifically what ' .
452
'kinds of files are viewable and/or ' .
453
'downloadable.', $app),
454
'backends.php' => sprintf('This file controls what backends are ' .
455
'available from %s.', $app),
456
'sources.php' => sprintf('This file defines the list of available ' .
457
'sources for %s.', $app)
459
$title = sprintf('%s is not properly configured', $app);
460
$header = sprintf('Some of %s\'s configuration files are missing or unreadable', $app);
461
$footer = sprintf('Create these files from their .dist versions in %s and change them according to your needs.', $appBase . '/config');
465
<head><title>$title</title></head>
466
<body style="background-color: white; color: black;">
470
foreach ($files as $file) {
471
$description = $descriptions[$file];
473
<h3>$file</h3><p>$description</p>
487
* Check the list of required Horde applications.
491
* @param array $app_list The application list.
493
* KEY: application name
494
* VALUE: An array with the following entries:
495
* 'error' -- Error Message
496
* 'version' -- The minimum version required
499
* @return string The HTML output.
501
function requiredAppCheck($app_list)
505
$apps = $this->applicationList();
507
foreach ($app_list as $key => $val) {
511
if (!isset($apps[$key])) {
512
$entry[] = $this->_status(false);
513
$entry[] = $val['error'];
515
/* Strip '-cvs' and H3 (ver) from version string. */
516
$appver = str_replace('-cvs', '', $apps[$key]->version);
517
$appver = preg_replace('/H3 \((.*)\)/', '$1', $appver);
518
if (version_compare($val['version'], $appver) === 1) {
519
$entry[] = $this->_status(false) . ' (Have version: ' . $apps[$key]->version . '; Need version: ' . $val['version'] . ')';
520
$entry[] = $val['error'];
522
$entry[] = $this->_status(true) . ' (Version: ' . $apps[$key]->version . ')';
525
$output .= $this->_outputLine($entry);
532
* Is this a 'recent' version of PEAR?
536
* @param boolean True if a recent version of PEAR.
538
function isRecentPEAR()
540
@include_once 'PEAR.php';
541
$pear_methods = get_class_methods('PEAR');
542
return (is_array($pear_methods) &&
543
(in_array('registershutdownfunc', $pear_methods) ||
544
in_array('registerShutdownFunc', $pear_methods)));
548
* Obtain information on the PHP version.
552
* @return object stdClass TODO
554
function getPhpVersionInformation()
556
$output = &new stdClass;
557
$url = urlencode($_SERVER['PHP_SELF']);
560
$output->phpinfo = $this->_testscript . '?mode=phpinfo&url=' . $url;
561
$output->extensions = $this->_testscript . '?mode=extensions&url=' . $url;
562
$output->version = phpversion();
563
$output->major = $this->_phpver['major'];
564
if (isset($this->_phpver['minor'])) {
565
$output->minor = $this->_phpver['minor'];
567
if (isset($this->_phpver['subminor'])) {
568
$output->subminor = $this->_phpver['subminor'];
570
$output->class = $this->_phpver['class'];
572
$output->status_color = 'red';
573
if ($output->major < '4.3') {
574
$output->status = 'This version of PHP is not supported. You need to upgrade to a more recent version.';
576
} elseif (($output->major == '4.3') || ($output->major == '4.4')) {
577
$output->status = 'You are running a supported version of PHP.';
578
$output->status_color = 'green';
579
} elseif ($output->major == '5.0') {
580
$output->status = 'PHP 5 is supported experimentally. You may not see any error messages, but the changed behaviour of objects in PHP 5 has unpredictable side effects.';
581
$ouput->status_color = 'orange';
583
$output->status = 'Wow, a mystical version of PHP from the future. Let <a href="mailto:dev@lists.horde.org">dev@lists.horde.org</a> know what version you have so we can fix this script.';
584
$output->status_color = 'orange';
588
$output->version_check = 'Horde requires PHP 4.3.0 or greater.';
595
* Get the application list.
599
* @return array List of stdClass objects.
600
* KEY: application name
601
* ELEMENT 'version': Version of application
602
* ELEMENT 'test': The location of the test script (if any)
604
function applicationList()
606
if (!empty($this->_appoutput)) {
607
return $this->_appoutput;
610
foreach ($this->applications as $mod => $det) {
611
if (($det['status'] != 'heading') &&
612
($det['status'] != 'block') &&
613
is_readable($det['fileroot'] . '/lib/version.php')) {
614
require_once $det['fileroot'] . '/lib/version.php';
615
eval('$defined = defined(\'' . strtoupper($mod) . '_VERSION\');');
617
eval('$ver = ' . strtoupper($mod) . '_VERSION;');
618
$this->_appoutput[$mod] = &new stdClass;
619
$this->_appoutput[$mod]->version = $ver;
620
if (($mod != 'horde') &&
621
@is_readable($det['fileroot'] . '/test.php')) {
622
$this->_appoutput[$mod]->test = $det['webroot'] . '/test.php';
628
return $this->_appoutput;
632
* Output the results of a status check.
636
* @param boolean $bool The result of the status check.
637
* @param boolean $required Whether the checked item is required.
639
* @return string The HTML of the result of the status check.
641
function _status($bool, $required = true)
644
return '<font color="green"><b>Yes</b></font>';
645
} elseif ($required) {
646
return '<font color="red"><b>No</b></font>';
648
return '<font color="orange"><b>No</b></font>';
653
* Internal output function.
657
* @param array $entry 3 element array.
660
* 2nd value: Test Result
661
* 3rd value: Error message (if present)
662
* 4th value: Error level (if present): 0 = error, 1 = warning
665
* @return string HTML output.
667
function _outputLine($entry)
669
$output = '<li>' . array_shift($entry) . ': ' . array_shift($entry);
670
if (!empty($entry)) {
671
$msg = array_shift($entry);
672
$output .= '<br /><font color="' . (empty($entry) || !array_shift($entry) ? 'red' : 'orange') . '"><b>' . $msg . '</b></font>' . "\n";
674
$output .= '</li>' . "\n";