Source Code Index
<?php
/*
	"NCSA httpd 1.3 would return some boring old error/problem message 
	which would often be meaningless to the user, and would provide no 
	means of logging the symptoms which caused it."
	  -- Apache, 'Custom Error Responses'
*/
/*
	"Once you are ready for deployment, you should either disable error 
	reporting completely by setting error_reporting() to 0, or turn off 
	the error display using the php.ini option display_errors, to insulate 
	your code from probing. If you choose to do the latter, you should also 
	define the path to your log file using the error_log ini directive, and 
	turn log_errors on."
	  -- PHP.NET
*/
 
/* ERROR.PHP - error and debug message handling
 
This gets included by INDEX.PHP and ADMIN.PHP (the two sole entry points) 
as the very first include; it provides two main things:
 
	Error and informational messages, both fatal and notices.
	Detailed debug/runtime message logging and displaying.
	A few server "error" functions.
 
Well, that's three. But we never said we could count...
*/
 
$GLOBALS['debug'] = '';
 
//ini_set('display_errors',1);
error_reporting(0);			// when upgrading use E_ERROR|E_PARSE
 
_error();				// initialize on first call
checkbaduser();				// checks banned IPs and Referers
					//  might exit
 
 
/* module support functions - see file MODULES */
 
function setmodule($file) {
	$GLOBALS['modules'][] = $file;
	$init = '_config_'.str_replace('.php','',$file);
	if (function_exists($init))
		$GLOBALS['functions'][] = $init;
}
 
function modules_init() {
	foreach ($GLOBALS['functions'] as $name) {
		debug($name);
		$name();
	}
}
 
function modules_loaded() {
	foreach ($GLOBALS['modules'] as $name)
		echo "\n<!-- $name -->";
}
//register_shutdown_function('modules_loaded');
// that will interfere with redirects and file downloads if registered
 
/* errorlog - set the errorlog level */
 
// called by CONFIG *if* logged in as Admin and 'debug =' is non-zero
 
function errorlog($log) {
 
	_error('error_log',$log);
	if ($log)
		register_shutdown_function('_msglogdump');
}
 
 
/* error - display a formated error message */
 
// we still only have a very clumsy way of handling errors...
 
function error($msg = '', $arg = '') {
 
	list($file,$line,$func) = caller(1);
	$func = caller(2,'function');
 
	if ($msg == '')
		$msg = "(empty message passed to error from: $file,$func)";
 
	switch ($msg) {					// specials
	case 'notfound':
		print "'$arg' not found";
		return;
	case 'last':
		if (($t = _error('last')))
			echo "last error: '$t'";
		return;
	case 'caller':
		$func = caller(3,'function');
		echo "called: $file,$line,$func $arg ";
		return;
	case -1:
		return _error();
	}
 
	if ($html = geterror($msg))			// special message?
		$msg = $html;
 
	_error('last',$msg);				// save message
 
	if ($arg === FALSE) {				// log message?
		if (_error('error_log'))
			_error('addmsg',$msg);
		return;
	}
 
	if ($arg === TRUE)				// return message?
		return $msg;
 
	if ($arg === -1) {
		$msg = obcomments($msg);
		echo "<!-- $msg -->";
		return;
	}
 
	if (debug()) {
		$bt = _backtrace();
		$msg .= "<div class='e'>(backtrace)<pre>$bt</pre></div>";
	}
 
	if ($arg == 'fatal') {				// early errors may be 
		if (!function_exists('displayhtml'))	//  before display.php
			exit($msg);			//  is loaded
		$t = 'fatalerror';
		if (headers_sent())
			$t = 'error';
		displayhtml($t,'$error',$msg);
		exit;
	}
 
	if ($arg == 'error') {
		displayhtml($arg,'$error',$msg);
		return;
	}
 
	if ($arg == 'html')
		$msg = "\n<!--\n".strip_htmlcomments($msg)."\n-->\n";
 
	print $msg;
}
 
 
/* debug - display message or add to message queue */
 
function debug($msg = NULL, $trunc = 0) {
 
	$error_log = _error('error_log');
 
	if ($msg === NULL)
		return $error_log;
 
	if ($error_log == 0 && $trunc != -1)
		return;
 
	list($file,$line,$func) = caller(1);
	$func = caller(2,'function');
 
	if (($t = config('debugmod')) && $t != $file)
		return;
	if (($t = config('debugnomod')) && strstr($t,$file))
		return;
 
	if ($msg === TRUE)
		$msg = '(true)';
	else
	if ($msg === FALSE)
		$msg = '(false)';
	else
	if (is_resource($msg))
		$msg = 'Resource: '. get_resource_type($msg);
	else
	if (is_object($msg))
		$msg = 'Object: '. get_class($msg);
	else
	if (is_array($msg))
		$msg = 'Array: {'. flatten($msg). '}';
 
	if ($trunc == 0 && config('debugtruncate'))
		$trunc = config('debugtruncate');
 
	if ($trunc && $trunc != -1 && strlen($msg) > $trunc)
		$msg = substr($msg,0,$trunc). ' ...';
 
	if (!config('nodebugentities'))
		$msg = htmlentities($msg);
 
	if (config('debugconvertnl'))
		$msg = str_replace("\n",'\n',$msg);
 
	$c = '';
	if (config('debugcaller')) {
		list($f,$l,$nu) = caller(2);
		$c = "($f,$l)";
	}
 
	if (!config('nodebugfunc'))
		$msg = "$c($file,$line,$func) $msg";
	else
		$msg = "$c($file,$line) $msg";
 
	if ($error_log == -1 || $trunc == -1)
		print $msg . '<br>';
	else
	if ($error_log == 100)
		print '<!-- '.$msg.' -->'.THIS_EOL;
	else
	if ($error_log == 200)
		$GLOBALS['debug'] .= $msg.'<br>'.THIS_EOL;
 
	else
		_error('addmsg',$msg);
}
 
 
/* redirect - send browser to a new location */
 
// assumes $url starts with a '?' or that path/file is valid
 
function redirect($url) {
 
	$host = 'http://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']);
	$host = rtrim($host,'/').'/';
 
	if (headers_sent($file,$line)) {
		debug("headers already sent by $file, line $line");
		debug(implode('<br>',headers_list()));
		print("Location: <a href='$url'>$url</a>");
		exit;
	}
 
	if (config('noredirect'))
		print("Location: <a href='$url'>$url</a>");
	else {
		// if debugging is on redirects fail with Opera, but we 
		// can just turn it off as the data would get lost anyway
		errorlog(0);
		header("Location: $host$url");
	}
	exit;
}
 
 
/* HTML error handler stuff */
 
 
/* htmlseterror - set globally accessible error message */
 
// this sets the "global" error message to one of the defined error messages
// see ERROR.INI - yes, it is a little odd
 
function htmlseterror($name, $id = NULL) {
 
	$error = _error();
 
	if (!$error) return FALSE;
 
	if ($id && strpos($error['id_list'],$id) !== FALSE) {
		$GLOBALS[$id] = $name;
		return;
	}
 
	if (!isset($error[$name]))
		$name = 'DEFAULT';
 
	if (!is_array($error[$name])) {
		$id = $error['default_id'];
		$ms = $error[$name];
	}
	else {
		$id = $error[$name]['id'];
		$ms = $error[$name]['msg'];
	}
 
	$GLOBALS[$id] = $ms;
 
	return $ms;
}
 
 
/* htmlgeterror - get a global error message based on ID/name lookup */
 
function htmlgeterror($name = NULL) {
 
	$error = _error();
 
	if (!$error) return FALSE;
 
	if ($name == NULL)
		$id = $error['default_id'];	// default "global" message
 
	if (strpos($error['id_list'],$name) !== FALSE)
		$id = $name;			// one of the "global" messages
 
	if (isset($id))				// did we get one
		return (isset($GLOBALS[$id])) ? $GLOBALS[$id] : "";
 
	if (!isset($error[$name]))		// no message?
		return "error '$name' error!";	//  oh no, an error error!
 
	if (is_array($error[$name]))		// one of comment/system
		return $error[$name]['msg'];	//  messages
 
	return $error[$name];			// an other message
}
 
 
/* geterror - just get the error message or not */
 
function geterror($name) {
 
	return _error($name);
}
 
 
/*
These are the "banning" check functions. checkbaduser() is always called early 
in the start process and checkbadinput() for each comment POST.
*/
 
 
/* checkbaduser - look for match of banned ip or user agent */
 
// right now a 404 is sent for the spammers so maybe they'll go away, but 
// that does not seem to works as some never stop trying; we want all of this 
// configurable, through Admin, who they are: agents, addresses, refers; and 
// what to do with them: 404, 403, redirect, send them megabytes of garbage...
 
function checkbaduser() {
 
	$e = _error();
 
	if (isset($e['HTTP_USER_AGENT']))
		foreach ($e['HTTP_USER_AGENT'] as $_)
			if (strstr($_SERVER['HTTP_USER_AGENT'],$_))
				notfound();
 
	if (isset($e['REMOTE_ADDR']))
		foreach ($e['REMOTE_ADDR'] as $_)
			if (strstr($_SERVER['REMOTE_ADDR'],$_))
				notfound();
 
	// some referers also suck
 
	if (isset($e['HTTP_REFERER']) && isset($_SERVER['HTTP_REFERER']))
		foreach ($e['HTTP_REFERER'] as $_)
			if (strstr($_SERVER['HTTP_REFERER'],$_))
				notfound();
 
}
 
/* checkbadinput - look for "banned words" */
 
// Comment spammers use the "[link=" and "[url=" strings. Those are the two 
// default "ban words" and the code stops if detected.
 
function checkbadinput($data) {
 
	if ($words = _error('BAN_WORDS'))
		foreach ($words as $k => $v)
			if (stristr($data,$v))
				forbidden();
}
 
 
function notfound($arg = NULL, $place = NULL) {
	if ($arg && !preg_match('/^http:/',$arg))
		$arg = "http://$arg";
	header('HTTP/1.1 404 Not Found');
echo "<!DOCTYPE HTML>
<html><head><title>404 Not Found</title></head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<ADDRESS>
Web Server at <a href='$arg'>$place</a>
</ADDRESS>
</body>
</html>
";
	exit;
}
function forbidden() {
	header("HTTP/1.1 403 Forbidden");
echo '<!DOCTYPE HTML>
<HTML><HEAD><TITLE>403 Forbidden</TITLE>
</HEAD><BODY>
<H1>That is Forbidden</H1>
You have done something wrong.
</BODY>
</HTML>
';
	exit;
}
 
 
/* "internal" functions (used here and nowhere else) */
 
 
/* _msglogdump - display the message log (as a shutdown fuction) */
 
function _msglogdump() {
 
	$e = _error('error_log');
	if ($e <= 0 || $e >= 100) return;		// special cases
 
	print THIS_EOL.
	"<div style='font-size:13px;background:white;color:black;'><pre>";
	foreach (_error('message_log') as $msg)
		print THIS_EOL.$msg;
	print "</pre></div>";
}
 
 
/* _backtrace - this version is vastly better than the last one! sheesh */
 
function _backtrace() {
 
	if (config('errorfullbacktrace'))
		return print_r(debug_backtrace(),1);
 
	ob_start();
	if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
		debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
	else
		debug_print_backtrace();
	$out = ob_get_contents();
	ob_end_clean();
 
	if (config('errorbacktrace'))
		return $out;
 
	$out = explode(PHP_EOL,$out);
	if (isset($out[3]))
		$bt[] = substr($out[3],2);
	$bt[] = substr($out[2],2);
	$s = '';
	foreach ($bt as $t) {
		$t = str_replace($_SERVER['DOCUMENT_ROOT'],'',$t);
		$t = str_replace(array('[',']'),'',$t);
		$s .= $t . '<br>';
	}
	return $s;
}
 
 
/* this module's config */
 
function _error($var = NULL, $val = NULL) {
static $error = array();
 
	if ($error == array()) {
 
		// this reads the error messages, but let's do it nicely 
		// it's an INI file that can be edited by Admin so if it 
		// errs we can continue
 
		// this is how to read a PHP format
		$e = @eval(file_get_contents('error.dat'));
 
		// this is the INI file format
		//$e = @parse_ini_file('error.ini',TRUE);
 
// tests show that reading the PHP file format is faster -- eventually this 
// code will just: include 'error.dat';
 
		if ($e) $error = $e;
		$error['error_log'] = 0;
		$error['message_log'][] = '<b>message log:</b>';
 
		if ($e == FALSE) {
			$e = error_get_last();
			$e['file'] = basename($e['file']);
			$m = "{$e['message']} ({$e['file']},{$e['line']})";
			$error['message_log'][] = $m;
		}
 
		$error['handler'] = set_error_handler('error_handler');
		return;
	}
 
	if ($var === NULL)
		return $error;
 
	// special case:
 
	if ($var == 'addmsg')
		return $error['message_log'][] = $val;
 
	// config values:
 
	if ($val !== NULL)
		return $error[$var] = $val;
		// caller must make sure to not override special keys
 
	if (isset($error[$var]))
		return $error[$var];
 
	return '';
 
}
 
/* error_handler - our error handler */
 
/*
PHP.NET: "The following error types cannot be handled with a user defined 
function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, 
E_COMPILE_WARNING..."
*/
 
function error_handler($errno, $errstr, $errfile, $errline) {
 
	$errfile = basename($errfile);
	$error = "$errstr ($errfile, $errline)";
 
	if ($errno == E_USER_ERROR)
        	exit($error);
 
	if ($errno == E_NOTICE) {
		if (config('shownotices'))
			echo($error.'<br>');
		if (config('debugnotices'))
			debug($error);
		return TRUE;
	}
 
	if (config('showerrors'))
		echo($error.'<br>');
 
	debug($error);
 
	return TRUE;
}
 
?>
 
THIS source compiler