# POPFILE LOADABLE MODULE
package POPFile::Logger;

use POPFile::Module;
@ISA = ("POPFile::Module");

#----------------------------------------------------------------------------
#
# This module handles POPFile's logger.  It is used to save debugging
# information to disk or to send it to the screen.
#
# Copyright (c) 2001-2006 John Graham-Cumming
#
#   This file is part of POPFile
#
#   POPFile is free software; you can redistribute it and/or modify it
#   under the terms of version 2 of the GNU General Public License as
#   published by the Free Software Foundation.
#
#   POPFile is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with POPFile; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#----------------------------------------------------------------------------

use strict;
use warnings;
use locale;

# Constant used by the log rotation code
my $seconds_per_day = 60 * 60 * 24;

#----------------------------------------------------------------------------
# new
#
#   Class new() function
#----------------------------------------------------------------------------
sub new
{
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = POPFile::Module->new();

    # The name of the debug file

    $self->{debug_filename__} = '';

    # The last ten lines sent to the logger

    $self->{last_ten__} = ();

    $self->{initialize_called__} = 0;

    bless($self, $class);

    $self->name( 'logger' );

    return $self;
}

#----------------------------------------------------------------------------
#
# initialize
#
# Called to initialize the interface
#
# ---------------------------------------------------------------------------
sub initialize
{
    my ( $self ) = @_;

    $self->{initialize_called__} = 1;

    # Start with debugging to file

    $self->global_config_( 'debug', 1 );

    # The default location for log files

    $self->config_( 'logdir', './' );

    # The output format for log files, can be default, tabbed or csv

    $self->config_( 'format', 'default' );

    # The log level.  There are three levels of log:
    #
    # 0   Critical log messages
    # 1   Verbose logging
    # 2   Maximum verbosity

    $self->config_( 'level', 0 );

    $self->{last_tickd__} = time;

    $self->mq_register_( 'TICKD', $self );

    return 1;
}

# ---------------------------------------------------------------------------
#
# deliver
#
# Called by the message queue to deliver a message
#
# There is no return value from this method
#
# ---------------------------------------------------------------------------
sub deliver
{
    my ( $self, $type, @message ) = @_;

    # If a day has passed then clean up log files

    if ( $type eq 'TICKD' ) {
        $self->remove_debug_files();
    }
}

#----------------------------------------------------------------------------
#
# start
#
# Called to start the logger running
#
#----------------------------------------------------------------------------
sub start
{
    my ( $self ) = @_;

    $self->calculate_today__();

    return 1;
}

# ---------------------------------------------------------------------------
#
# service
#
# ---------------------------------------------------------------------------
sub service
{
    my ( $self ) = @_;

    $self->calculate_today__();

    # We send out a TICKD message every hour so that other modules
    # can do clean up tasks that need to be done regularly but not
    # often

    if ( time > ( $self->{last_tickd__} + 3600 ) ) {
        $self->mq_post_( 'TICKD' );
        $self->{last_tickd__} = time;
    }

    return 1;
}

# ---------------------------------------------------------------------------
#
# calculate_today
#
# Set the global $self->{today} variable to the current day in seconds
#
# ---------------------------------------------------------------------------
sub calculate_today__
{
    my ( $self ) = @_;

    # Create the name of the debug file for the debug() function
    $self->{today__} = int( time / $seconds_per_day ) * $seconds_per_day;

    # Note that 0 parameter than allows the logdir to be outside the user
    # sandbox

    $self->{debug_filename__} = $self->get_user_path_(
        $self->config_( 'logdir' ) . "popfile$self->{today__}.log", 0 );
}

# ---------------------------------------------------------------------------
#
# remove_debug_files
#
# Removes popfile log files that are older than 3 days
#
# ---------------------------------------------------------------------------
sub remove_debug_files
{
    my ( $self ) = @_;

    my @debug_files = glob( $self->get_user_path_(
                          $self->config_( 'logdir' ) . 'popfile*.log', 0 ) );

    foreach my $debug_file (@debug_files) {
        # Extract the epoch information from the popfile log file name
        if ( $debug_file =~ /popfile([0-9]+)\.log/ )  {
            # If older than now - 3 days then delete
            unlink($debug_file) if ( $1 < (time - 3 * $seconds_per_day) );
        }
    }
}

# ----------------------------------------------------------------------------
#
# debug
#
# $level      The level of this message
# $message    A string containing a debug message that may or may not be 
#             printed
#
# Prints the passed string if the global $debug is true
#
# ----------------------------------------------------------------------------
sub debug
{
    my ( $self, $level, $message ) = @_;

    if ( $self->{initialize_called__} == 0 ) {
        return;
    }

    if ( $level > $self->config_( 'level' ) ) {
        return;
    }

    if ( $self->{debug_filename__} eq '' ) {
        return;
    }

    if ( $self->global_config_( 'debug' ) > 0 ) {

        # Check to see if we are handling the USER/PASS command and if
        # we are then obscure the account information

        if ( $message =~ /((--)?)(USER|PASS)\s+\S*(\1)/i ) {
            $message = "$`$1$3 XXXXXX$4";
        }

        $message =~ s/([\x00-\x1f])/sprintf("[%2.2x]", ord($1))/eg;

        my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
            localtime;
        $year += 1900;
        $mon  += 1;

        $min  = "0$min"  if ( $min  < 10 );
        $hour = "0$hour" if ( $hour < 10 );
        $sec  = "0$sec"  if ( $sec  < 10 );

        my $delim = ' ';
        $delim = "\t" if ( $self->config_( 'format' ) eq 'tabbed' );
        $delim = ',' if ( $self->config_( 'format' ) eq 'csv' );

        my $msg =
            "$year/$mon/$mday$delim$hour:$min:$sec$delim$$:$delim$message\n";

        if ( $self->global_config_( 'debug' ) & 1 )  {
              if ( open DEBUG, ">>$self->{debug_filename__}" ) {
                print DEBUG $msg;
                close DEBUG;
            }
        }

        print $msg if ( $self->global_config_( 'debug' ) & 2 );

        # Add the line to the in memory collection of the last ten
        # logger entries and then remove the first one if we now have
        # more than 10

        push @{$self->{last_ten__}}, ($msg);

        if ( $#{$self->{last_ten__}} > 9 ) {
            shift @{$self->{last_ten__}};
        }
    }
}

# GETTERS/SETTERS

sub debug_filename
{
    my ( $self ) = @_;

    return $self->{debug_filename__};
}

sub last_ten
{
    my ( $self ) = @_;

    if ( $#{$self->{last_ten__}} >= 0 ) {
        return @{$self->{last_ten__}};
    } else {
        my @temp = ( 'log empty' );
        return @temp;
    }
}

1;
