Current File : /home/inlingua/public_html/decay_sym/root/var/softaculous/apps/exim/popb4smtp-watch
#!/usr/bin/perl -w

use strict;
use Date::Parse;
use File::Tail;
use Unix::Syslog qw(:macros :subs);

$SIG{INT} = sub {
	syslog(LOG_CRIT, "%s", "exiting on SIGINT\n");
	exit 1;
};
$SIG{TERM} = sub {
	syslog(LOG_CRIT, "%s", "exiting on SIGTERM\n");
	exit 1;
};

my $DSEARCHDIR = '/var/webuzo-data/popb4smtp';
my $DEBIANLOG = '/var/log/mail.log';
my $POPLOG = '/var/log/maillog';
my $PROGNAME = 'popb4smtp-watch';
my $PIDFILE = '/var/run/popb4smtp-watch.pid';

sub dsearch_store_popauth($$$);
sub mkdirp($);
sub select_readable(\@);
sub xdie(@);
sub xwarn(@);

my (@readable_tails, @tail_refs);

openlog($PROGNAME, LOG_PERROR | LOG_PID, LOG_MAIL);
open(PID, "> $PIDFILE") or
    xdie "$PIDFILE: $!\n";
print PID "$$\n";
close(PID);

foreach my $fn ($DEBIANLOG, $POPLOG) {
	my $tail;

	$tail = File::Tail->new(
		name => $fn,
		maxinterval => 1,
		interval => 1,
		ignore_nonexistant => 1
	    );
	push(@tail_refs, $tail);
}
syslog(LOG_CRIT, "%s", "tailing files: $DEBIANLOG, $POPLOG\n");

while (1) {
	@readable_tails = select_readable(@tail_refs);
	if (not @readable_tails) {
		# Give up our time slice so that the kernel treats us like
		# a model citizen with respect to CPU utilization.
		#
		select(undef, undef, undef, 0.25);
		next;
	}

	foreach my $tail (@readable_tails) {
		my ($ip, $line, $timestr, $unixtime, $user);

		$line = $tail->read();
		chomp($line);

		if ($line =~ /^(\w+\s+\d+\s+\d+:\d+:\d+).*user=<(.*?)>.*rip=(.*?),/ or
		    $line =~ /^(\w+\s+\d+\s+\d+:\d+:\d+) .* imapd: LOGIN, user=([^,]+), ip=\[.+:([0-9.]+)\]/) {
			($timestr, $user, $ip) = ($1, $2, $3);
			$unixtime = str2time($timestr);
			dsearch_store_popauth($unixtime, $user, $ip);
		}
	}
}


sub dsearch_store_popauth($$$)
{
	my ($unixtime, $user, $ip) = @_;
	my ($subdir);

	$ip =~ /(\d)$/;
	$subdir = $1;
	if (not mkdirp("$DSEARCHDIR/$subdir")) {
		# mkdirp() logs its own warnings for us
	} elsif (not open(IPFILE, "> $DSEARCHDIR/$subdir/$ip")) {
		xwarn "$DSEARCHDIR/$subdir/$ip: $!\n";
	} elsif (not print IPFILE "$unixtime:$user:$ip\n") {
		xwarn "$DSEARCHDIR/$subdir/$ip: $!\n";
	} elsif (not close(IPFILE)) {
		xwarn "$DSEARCHDIR/$subdir/$ip: $!\n";
	}
}


sub mkdirp($)
{
	my ($dir) = @_;
	my ($i, @parts, $path);

	@parts = split(/\//, $dir);
	if (not @parts or (@parts == 1 and not $parts[0])) {
		xdie "panic: empty path passed to mkdirp()\n";
	}
	if (not $parts[0]) {
		$path = '/';
		shift @parts;
	} else {
		$path = '';
	}
	for ($i = 0; $i < @parts; $i++) {
		if ($i > 0) {
			$path .= "/$parts[$i]";
		} else {
			$path .= $parts[$i];
		}
		if (not -e $path) {
			if (not mkdir($path, 0777)) {
				xwarn "$path: $!\n";
				return 0;
			}
		} elsif (not -d $path and not -l $path) {
			return 0;
			xwarn "$path: exists and is not a directory\n";
		}
	}

	return 1;
}


# Given a list of File::Tail object references, block until one or more of
# the objects becomes readable and then return the list of readable objects.
#
sub select_readable(\@)
{
	my ($tails) = @_;
	my ($nfound, $timeleft, @pending);

	($nfound, $timeleft, @pending) = File::Tail::select(
		undef, undef, undef, undef, @{$tails}
	    );

	return @pending;
}


sub xdie(@)
{
	my (@msg) = @_;
	my ($msgstr, $rv);

	$rv = $! or 1;
	$msgstr = join(' ', @msg);
	chomp($msgstr);
	syslog(LOG_CRIT, "%s", $msgstr);
	exit $rv;
}


sub xwarn(@)
{
	my (@msg) = @_;
	my ($msgstr);

	$msgstr = join(' ', @msg);
	chomp($msgstr);
	syslog(LOG_CRIT, "%s", $msgstr);
}