use lib "/home/exelana0/public_html/lib";
use CGI::Carp qw(fatalsToBrowser set_die_handler noHeader);
use Devel::StackTrace;
package TDL::Error;
# Automated error reporting
# Export the error subroutine
use Exporter;
@ISA = "Exporter";
@EXPORT = qw(error);
use strict;
our ($cgi, $count, @email, $emailFrom, @errors, $fullReport, $html, $logFile, $logOffset, $logOpen,
$needHeader, $outputSafe, $q, $VERSION, %SRC);
BEGIN
{
@errors = ();
@email = ();
$emailFrom = $ENV{SERVER_ADMIN} | "";
$VERSION = "2.0";
$cgi = 0;
$count = 0;
$fullReport = 0;
$html = 1;
$logFile = "/usr/local/apache/logs/error_log";
$logOffset = -s $logFile;
$logOpen = 0;
$needHeader = 1;
$outputSafe = 0;
$q = undef;
%SRC = ();
}
END
{
our @notify = ();
if (scalar(@email) > 0 && $emailFrom ne "")
{
eval("use TDL::Admin");
eval("\@notify = TDL::Admin::adminNotify()");
if (scalar(@notify) > 0)
{
my $subject = shift @email;
my $last = pop @email;
push @email, $last if $last ne "
";
if (open(MAIL, "|/usr/sbin/sendmail -t -i"))
{
map { print MAIL "To: $_\n"; } @notify;
print MAIL "From: $emailFrom\n";
print MAIL "Subject: $subject\n";
print MAIL "Content-Type: text/html\n\n";
print MAIL "$subject";
map { print MAIL "$_\n" } @email;
print MAIL "\n";
close MAIL;
}
}
}
}
our $first = 1;
sub TDL_Error_trace
{
return; # disable tracing
return unless $outputSafe;
print "
" if $first;
$first = 0;
print "",join(" ",@_),"
\n";
}
sub init_log
{
my ($log) = @_;
$logOpen = 1;
my $prevFH = select TDL_ERROR; # Disable buffering
$| = 1;
select $prevFH;
print TDL_ERROR '='x80,"\n$ENV{REQUEST_URI}\n";
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time());
my $now = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
print TDL_ERROR "$now\n";
map { my $v = $ENV{$_}; $v =~ s/\n/\\n/; printf TDL_ERROR "%s: %s\n", $_, $ENV{$_} } sort keys %ENV;
print TDL_ERROR "admin_user: $TDL::Admin::isAdminUser\n";
print TDL_ERROR "admin_ip: $TDL::Admin::isAdminIP\n";
print TDL_ERROR "fullReport: $fullReport\n";
print TDL_ERROR "verbose: $Carp::Verbose\n" if defined($Carp::Verbose);
push @email, "Error details recorded in $log at $now
";
}
# output error messages
sub error
{
my($error_message) = @_;
# If the Admin module is installed and it reports that the current user
# is an administrator then allow full reports.
eval("use TDL::Admin");
eval("\$fullReport = TDL::Admin::isAdmin()");
$count++;
if ($needHeader)
{
TDL_Error_trace("need header");
# ----- Determine if the cgilib has already printed a header -----
if (defined($main::cgilib)
&& defined($main::cgilib{header})
&& defined($main::cgilib{header}->{done}))
{
$cgi = 1;
$needHeader = !$main::cgilib{header}->{done};
$html = ($main::cgilib{header}->{type} eq "text/html" ? 1 : 0);
}
# ----- Determine if the CGI library has already printed a header -----
if ($needHeader && %CGI::)
{
eval
{
eval("use Devel::Symdump");
my $main = Devel::Symdump->new("main");
map {
unless (m/::(?:_\<|\$|\@|\"|\\|[\x00-\x1f])/)
{
unless (defined($q))
{
eval("\$q = \$$_") if ref(eval("\$$_")) eq "CGI";
}
}
} $main->scalars;
};
$needHeader = 0 if defined($q) && !defined($q->{'.header_printed'});
}
}
# ----- Emit a header (using an appropriate library) -----
my $printedHeader = 0;
if ($needHeader)
{
TDL_Error_trace("emit header");
if ($cgi)
{
main::headTitle("CGI Error");
main::headEmit();
} elsif (defined($q)) {
print $q->start_html("CGI Error"), "\n";
} else {
print STDOUT "Content-type: text/html\n\nCGI Error\n";
}
$printedHeader = 1;
$outputSafe = 1;
$needHeader = 0;
}
# ----- Print the preface -----
if ($count < 2)
{
TDL_Error_trace("emit preface");
if (defined($ENV{REQUEST_URI}))
{
push @email, "CGI Error in $ENV{REQUEST_URI}";
push @email, "CGI Error in $ENV{REQUEST_URI}
";
} else {
push @email, "Error in $0";
}
if ($html)
{
print STDOUT "
" unless $printedHeader;
print STDOUT "CGI Error in $ENV{REQUEST_URI}
\n",
"Sorry, the following error has occurred:
\n";
} else {
print STDOUT '-'x60,"\n" unless $printedHeader;
print STDOUT "CGI Error in $ENV{REQUEST_URI}\n",
"Sorry, the following error has occurred:\n";
}
}
# ----- Record script failures for the system administrator to examine -----
unless ($logOpen)
{
my ($log) = $ENV{DOCUMENT_ROOT} =~ m/^(.*)\/[^\/]+$/;
$log .= "/dump/error_log";
TDL_Error_trace("open log $log");
init_log($log) if open(TDL_ERROR, ">>$log");
}
TDL_Error_trace("no message") unless defined($error_message);
return unless defined($error_message);
# ----- Emit the short form of the error message -----
my @src = ();
my @messages = split(/\n/, $error_message);
my $i = 0;
while ($i < scalar(@errors) && $i < scalar(@messages) && $messages[$i] eq $errors[$i])
{
$i++;
}
print TDL_ERROR '-'x10,"\n" if $i < scalar(@messages);
while ($i < scalar(@messages))
{
push @email, filterHTML($messages[$i])."
";
push @errors, $messages[$i];
TDL_Error_trace("message: $messages[$i]") if $logOpen;
print TDL_ERROR "$messages[$i]\n" if $logOpen;
push @src, $1, $2 if $messages[$i] =~ /\sat\s+(.*)\s+line\s+(\d+)/;
$messages[$i] =~ s/\s+at\s+\S+\s+line\s+\d+//g unless $fullReport;
if ($html)
{
print STDOUT "".filterHTML($messages[$i])."
\n";
} else {
print STDOUT "$messages[$i]\n";
}
$i++;
}
# ----- Record stack trace -----
if ($logOpen)
{
TDL_Error_trace("stack trace");
my $trace = Devel::StackTrace->new;
my $string = $trace->as_string;
$string =~ s/\n+$//;
print TDL_ERROR $string,"\n",'-'x10,"\n";
}
# ----- Output new entries from the system error log -----
if (defined($logOffset) && $logOffset < -s $logFile)
{
TDL_Error_trace("system log");
if (!open (F, $logFile))
{
print STDOUT "Unable to open error log: $!
\n";
} else {
tagOutput("h2","Web Server Error Log") if $fullReport;
seek F, $logOffset, 0;
while (my $line = )
{
chomp $line;
print STDOUT $html ? filterHTML($line)."
\n" : "$line\n" if $fullReport;
print TDL_ERROR "$line\n" if $logOpen;
push @src, $1, $2 if $line =~ m/\sat\s+(.*)\s+line\s+(\d+)/;
}
close F;
# Reset the log location so that subsequent calls start from the new
# location.
$logOffset = -s $logFile;
}
}
# Output the source lines for any error messages
for (my $index = 0; $index < scalar(@src); $index += 2)
{
print "\n" unless $fullReport;
showSource($src[$index], $src[$index + 1]);
}
}
sub tagOutput
{
my ($tag,$message) = @_;
print STDOUT "<$tag>" if $html;
print STDOUT $message;
print STDOUT $html ? "$tag>\n" : "\n";
}
# Show a statement from a source file (starting at a specific line)
sub showSource
{
local *S;
my ($filePath, $lineNum) = @_;
my $marker = "$filePath\t$lineNum";
return if defined($SRC{$marker});
TDL_Error_trace("source: $filePath at $lineNum");
if (open (S, $filePath))
{
my ($shortName) = $filePath =~ /([^\/]+)$/;
tagOutput("h3","Source code ($shortName at $lineNum)") if $fullReport;
print TDL_ERROR "Source code ($shortName at $lineNum)\n";
push @email, "Source code ($shortName at $lineNum)
";
while (my $line = )
{
chomp $line;
if ($lineNum-- <= 1)
{
print $html ? filterHTML($line)."
\n" : "$line\n" if $fullReport;
print TDL_ERROR "$line\n" if $logOpen;
push @email, filterHTML($line)."
";
if ($line =~ /(?:;|})\s*(?:\#|$)/)
{
print STDOUT "
\n" if $fullReport && $html;
push @email, "
";
last;
}
}
}
close S;
$SRC{$marker} = 1;
}
}
sub filterHTML
{
my ($str) = @_;
$str =~ s/\&/&/g;
$str =~ s/\</g;
$str =~ s/\>/>/g;
return $str;
}
##############################################################################
# Handle error messages
BEGIN
{
sub die_handler
{
TDL::Error::error(@_);
}
CGI::Carp::set_message(\&die_handler);
CGI::Carp::set_die_handler(\&die_handler);
}
# End of TDL::Error
1;