#!/usr/bin/env perl

# $Id: clean_rtpproxy_attrs,v 1.3 2009/07/15 23:02:15 tma0 Exp $

# The script cleans up orphaned RTP proxy attributes from rtpproxy_attrs.
# The orphaned attribute is the attribute having none of its sessions.
# in iptrtpproxy session list.

use warnings;
use strict;
use DBI;

my $version = "0.2";
my $defConfigFile = "/etc/iptrtpproxy.cfg";
my $configFile = $defConfigFile;
my $verbose = 0;

my $defmysqlhost = 'localhost';
my $mysqlhost = $defmysqlhost;
my $defmysqldb = 'ser';
my $mysqldb = $defmysqldb;
my $defmysqluser = 'ser';
my $mysqluser = $defmysqluser;
my $defmysqlpassword = 'heslo';
my $mysqlpassword = $defmysqlpassword;
my $defmysqlsock = "";
my $mysqlsock = $defmysqlsock;

use Config::IniFiles;

sub dbg {
	if ($verbose) {
		print STDERR "DBG: $_[0]\n";
	}
}

sub myexit ($$)
{
	my ($msg, $exitcode ) = @_;
	print "# ERROR: $msg\n";
	DBI->disconnect_all();
	exit $exitcode;
}


sub do_exec($) 
{
	my $cmd = $_[0];
	dbg "Exec: $cmd";
	my @res = `$cmd`;
	dbg "Exit code: $?";	
	@res;
}


# parse config file to $cfg
sub parseConfigFile($)
{
	my $fileName = $_[0];
	dbg "Parsing config file '$fileName'\n" if $verbose;
	my %switchboards = ();
	goto ret unless -e $configFile;
	my $cfg = Config::IniFiles->new( -file => $configFile, -default => "<default>" );

	for my $section ($cfg->Sections) {
		if ($section =~ /^switchboard:.+$/) {
			my $name = $section;
			$name =~ s/^switchboard://;
			print STDERR "Parsing section '$section', name='$name'\n" if $verbose;
			for my $param ('addr', 'port') {
				my $val = $cfg->val($section, $param);
				if ($val) {
					$switchboards{$name}{$param} = $val;
				}
			}
		}

	}
ret:
	return %switchboards;
}

my %skip;
sub Config_Extend_MySQL__read_config {
# copy pasted not to add another dependency
    my ($what, $path) = @_;
    my $content = "";
    my $opts = {}; #{ err_mode => "quiet" };

use File::Basename          qw(dirname);
use File::Spec::Functions   qw(catfile rel2abs);

    if ($what eq "file") {
        my $base_dir = dirname($path);
        open (MYFILE, $path);
        while (<MYFILE>) {
	 	$content .= $_;
	}
	close (MYFILE); 
        # handle single param (without value)
        $content =~ s{^ \s* (\w+ (?:-\w+)* ) \s* $}{$1 = yes}xgm;
        # handle includes
        $content =~ s{^ \s* !include(dir)? \s+ (.+) \s* $}
                     { __read_config($1 || "file", rel2abs($2, $base_dir)) }xgme;
    }
    elsif ($what eq "dir") {
        opendir(my $dirh, $path) or return "";

        while (my $file = readdir($dirh)) {
            # skip invisible files and directories we shouldn't 
            # recurse into, like ../ or CVS/
            next if $skip{$file} or index($file, ".") == 0;

            my $filepath = catfile($path, $file);

            if (-f $filepath) {
                $content .= Config_Extend_MySQL__read_config(file => $filepath)
            }
            elsif (-d _) {
                $content .= Config_Extend_MySQL__read_config(dir => $filepath)
            }
        }

        closedir($dirh);
    }

    return $content
}



my $scriptName = $0;
$scriptName =~ s!^.*/!!;

sub printUsage {
	print "$scriptName, Version: $version\n";
	print "usage: $scriptName [-f <iptrtpproxy_config>] [-v] [-h] [-M <mysql_conf>[-H <db_host>] [-S <db_sock>] [-D <db_name>] [-u <db_user>] [-p <db_password>]\n";
	print "  iptrtpproxy_config .. iptrtpproxy_helper config, default: $defConfigFile\n";
	print "  mysql_conf .. database mysql.conf to get connection params\n";
	print "  db_host .. database MYSQL  host, default: $defmysqlhost\n";
	print "  db_sock .. MYSQL socket, default: $defmysqlsock\n";
	print "  db_name .. database name, default: $defmysqldb\n";
	print "  db_user .. database user, default: $defmysqluser\n";
	$_ = substr $defmysqlpassword, 1, length($defmysqlpassword)-2;
	s/./\*/g;
	my $psw = substr($defmysqlpassword, 0, 1) . $_ . substr($defmysqlpassword, length($defmysqlpassword)-1, 1);
	print "  db_password .. database password, default: $psw\n";
	print "  -v .. verbose\n";
}

my $command = '';
my $arg;

while ($#ARGV >= 0) {
	$arg = shift(@ARGV);
	if ($arg eq '-f') {
		if ($#ARGV < 0) {
			print STDERR "ERROR: config file name required\n";
			&printUsage();
			exit(1);
		} else {
			$configFile = shift(@ARGV);
		}
	} elsif ($arg eq '-M') {
		if ($#ARGV < 0) {
			print STDERR "ERROR: mysql conf file name required\n";
			&printUsage();
			exit(1);
		} else {
			my $mysqlconf = shift(@ARGV);
			dbg "Parsing mysql config file '$mysqlconf'\n" if $verbose;
			if (-e $mysqlconf) {

				# read the file and resolve the MySQL-isms
				my $content = Config_Extend_MySQL__read_config('file', $mysqlconf);
				my $fh = undef;
				if ($] < 5.008) {
				        require IO::String;
					$fh = IO::String->new(\$content);
				}
				else {
					open($fh, "<", \$content);
				}
				my $cfg = Config::IniFiles->new( -file => $fh );
				if (!$cfg) {
					print STDERR "ERROR: @Config::IniFiles::errors\n";
				}
				else {
					my $val;
					$val = $cfg->val('client', 'database');
					$mysqldb = $val if $val;
					$val = $cfg->val('client', 'user');
					$mysqluser = $val if $val;
					$val = $cfg->val('client', 'password');
					$mysqlpassword = $val if $val;
					$val = $cfg->val('client', 'socket');
					$mysqlsock = $val if $val;
				}
			} else {
				print STDERR "ERROR: mysql conf '$mysqlconf' not found\n"
			}
		}
	} elsif ($arg eq '-D') {
		if ($#ARGV < 0) {
			print STDERR "ERROR: database name required\n";
			&printUsage();
			exit(1);
		} else {
			$mysqldb = shift(@ARGV);
		}
	} elsif ($arg eq '-u') {
		if ($#ARGV < 0) {
			print STDERR "ERROR: user name required\n";
			&printUsage();
			exit(1);
		} else {
			$mysqluser = shift(@ARGV);
		}
	} elsif ($arg eq '-p') {
		if ($#ARGV < 0) {
			print STDERR "ERROR: password required\n";
			&printUsage();
			exit(1);
		} else {
			$mysqlpassword = shift(@ARGV);
		}
	} elsif ($arg eq '-H') {
		if ($#ARGV < 0) {
			print STDERR "ERROR: hostname required\n";
			&printUsage();
			exit(1);
		} else {
			$mysqlhost = shift(@ARGV);
		}
	} elsif ($arg eq '-S') {
		if ($#ARGV < 0) {
			print STDERR "ERROR: socket required\n";
			&printUsage();
			exit(1);
		} else {
			$mysqlsock = shift(@ARGV);
		}
	} elsif ($arg eq '-v') {
		$verbose++;
	} elsif ($arg eq '-h') {
		&printUsage();
		exit(0);
	} else {
		print STDERR "ERROR: unknown argument '$arg'\n";
		&printUsage();
		exit(1);
	}	
}

dbg "Verbose: $verbose";

my $fileName = $_[0];

dbg "Connecting: mysql://$mysqluser\@$mysqlhost/$mysqldb";
myexit("Cannot connect database", 2) unless my $dbh = DBI->connect("dbi:mysql:database=$mysqldb".
	($mysqlhost?";host=$mysqlhost":'').
	($mysqlsock?";mysql_socket=$mysqlsock":'')
	, $mysqluser, $mysqlpassword);

dbg "Reading rtpproxy_attrs";
my %attrs = ();
# copy rtpproxy_attrs to memory, we need snapshot of table
my $sth = $dbh->prepare("SELECT * FROM rtpproxy_attrs");
$sth->execute();
my $n = 0;
while (my $href = $sth->fetchrow_hashref()) {

	if ($href->{'name'} eq 'dlg_rtp_sess_ids') {
		$attrs{$href->{'id'}} = $href->{'value'};
		$n++;
	}
}
$sth->finish();
dbg "rtpproxy_attrs records: $n";

my %cfgSwitchboards = &parseConfigFile($configFile);

my %switchboards = ();
for my $id (keys %cfgSwitchboards) {
	dbg "Switchboard: $id";
	my $cmd = "iptrtpproxy list --addr-a $cfgSwitchboards{$id}{'addr'} --port-a $cfgSwitchboards{$id}{'port'} --format tab --sessions";
	my @lines = &do_exec($cmd);
	my @l = split(/\t/, $lines[0]);
	my $sess_id = -1;
	my $tick = -1;
	for my $i (0..$#l) {
		if ($l[$i] eq 'sess-id-a') {
			$sess_id = $i;
		} elsif ($l[$i] eq 'created/tick') {
			$tick = $i;
		}
	}
	dbg "Lines: $#lines, Columns: sess-id: $sess_id, tick: $tick";
	for my $i (1..$#lines) {
		@l = split(/\t/, $lines[$i]);
		$switchboards{$id}{$l[$sess_id]} = $l[$tick];
	}
}

dbg "Clean rtpproxy_attrs";
# get iptrtpproxy switchboards
my $delCnt = 0;
$sth = $dbh->prepare("DELETE FROM rtpproxy_attrs WHERE id=?");
loop: for my $id (keys %attrs) {
	# format name:session_id/stamp (, session_id/stamp)*] 
	my $found = 0;
	my ($name, $s) = split( /:/, $attrs{$id});
	if (!defined $s) {
		goto del;
	}
	my @ss = split( /,/, $s);
	for my $i (0..$#ss) {
		my ($sess_id, $tick) = split ( /\//, $ss[$i]);
		if ($sess_id eq "" || !defined $tick || $tick eq "") {
			next;
		}
		if ($switchboards{$name}{$sess_id} && $switchboards{$name}{$sess_id} eq $tick) {
			dbg "Leave: $id=$attrs{$id}";
			next loop;
		}
	}
del:
	dbg "Delete: $id=$attrs{$id}";
	$sth->execute($id);
	$delCnt++;
}
dbg "Deleted: $delCnt records";

exit(0);