#!/usr/bin/perl -w
#
# /usr/local/sbin/proxy-manager
#
# proxy-manager Programm zum Verwalten der Internetfreigaben
#
# (c) 2001 Thomas Bleher <ThomasBleher@gmx.de>
# (c) 2002,2003 Andreas Dangel <adabolo@adabolo.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#


use Fcntl;   # for sysopen, file modes
use Fcntl qw(:flock);
use strict;

## Konfiguration
my $version = "1.2 (2003-08-21)";
my $pidfile = '/var/run/proxy-manager.pid';
my $logfile = '/var/log/internet-freigabe';
my $iptables = '/sbin/iptables';
my $actives = '/var/state/internet-freigabe';
my $lockfile = '/var/lock/proxy-manager.lck';
my $lockfile2 = '/var/lock/proxy-manager2.lck';
my $pid;


#########################
##### SUBPROCEDURES #####
#########################
sub usage() {
    print <<EOF;
Syntax: $0 [-hdocak] [login] [ip-adresse] [zeitsekunden]

    -h   Dieser Hilfetext
    -d   der eigentliche proxy-manager wird im Hintergrund gestartet
    -o   ffnet eine Verbindung fr "ip-adresse" und schliet sie
         automatisch, wenn "zeitsekunden" erreicht sind
    -c   schliet die Verbindung fr "ip-adresse" manuell
    -a   prft, ob fr "ip-adresse" eine Verbindung besteht
    -k   beendet den proxy-manager und lscht alle Verbindungen
    -t   tested, ob der proxy-manager im Hintergrund luft
         (siehe /var/run/proxy-manager.pid).

    [login]
         Der Benutzer, der eine Verbindung ffnet, etc. wird geloggt.

    [ip-adresse]
         Jede beliebige Form ist erlaubt:
         Beispiel: 192.168.0.168
         Es kann auch 'all' verwendet werden.

    [zeitsekunden]
         Anzahl der vergangenen Sekunden seit seit 00:00:00, Jan 1, 1970.
         Zu ermitteln z.B. durch "date +%s".

    Hinweise:
       Falls Fehler beim Start des proxy-managers auftreten
       sollten: die Zugriffsrechte berprfen:
       $logfile
       $actives


    proxy-manager $version
    (c) 2001 Thomas Bleher <ThomasBleher\@gmx.de>
    (c) 2002,2003 Andreas Dangel <adabolo\@adabolo.de>

    proxy-manager comes with ABSOLUTELY NO WARRANTY.
    This is free software, and you are welcome to redistribute it
    under certain conditions; see the GNU GPL.
EOF

    exit;
}

sub log() {
    my $data = shift;
    chomp($data);
    my $datum = localtime(time);
    open(S, ">>$lockfile");
    flock(S, LOCK_EX);
    open(LOG, ">>$logfile");
    print LOG "$datum: $data\n";
    close(LOG);
    close(S);
}

sub open_proxy() {
    &log("open_proxy()");
}

sub close_proxy() {
    &log("close_proxy()");
}

sub kill_proxymanager() {
    &close_proxy();
    unlink $actives;
    system("touch $actives");

    unlink($pidfile);

    exit;
}

sub open_connection() {
    my $login;
    my $ip;
    my $time;
    if (not (($login, $ip, $time) = @_)) {
        &log("open_connection(): missing arguments!");
        return;
    }
    if ($ip =~ /[^\w.]/) {
        &log("open_connection(): invalid argument! $ip");
        return;
    }

    &log("open_connection(): $login: $ip, $time");

    &add_actives($ip, $time);
}

sub active_connection() {
    my $ip;
    if (not ($ip = shift)) {
        &log("active_connection(): missing arguments!");
        return;
    }
    if ($ip =~ /[^\w.]/) {
        &log("active_connection(): invalid argument! $ip");
        return;
    }


    return &find_actives($ip);
}

sub close_connection() {
    my $ip;
    if (not ($ip = shift)) {
        &log("close_connection(): missing arguments!");
        return;
    }
    if ($ip =~ /[^\w.]/) {
        &log("close_connection(): invalid argument! $ip");
        return;
    }

    &log("close_connection() for $ip");

    &remove_actives($ip);
}

sub check_daemon() {
    if (-e $pidfile) {
        return 0;
    }
    return 1;
}

sub start_daemon() {
    if (not defined($pid = fork)) {
        die "Couldn't fork!";
    }

    if ($pid) {  #parent
        exit;
    } else {     #child
        sysopen(PIDFILE, $pidfile, O_WRONLY | O_CREAT | O_EXCL) || die "Instance of proxy-manager already running! See $pidfile";
        print PIDFILE $$;
        close PIDFILE;

        while(1) {
            sleep(60);
            my $time = time;
            &remove_actives_by_time($time);
        }
    }
}

sub add_actives() {
    my $ip = shift;
    my $time = shift;

    open(S, ">>$lockfile2");
    flock(S, LOCK_EX);
    open(FILE, "<$actives");
    my @data = <FILE>;
    close(FILE);

    push @data, "$ip:$time";

    open(FILE, ">$actives");
    print FILE join("\n", @data);
    close(FILE);

    close(S);

}

sub find_actives() {
    my $ip = shift;

    #&log("find_actives() for $ip");

    open(S, ">>$lockfile2");
    flock(S, LOCK_EX);
    open(FILE, "<$actives");
    my @data = <FILE>;
    close(FILE);

    my @found = grep(/$ip/, @data);
    my @found2 = grep(/all/, @data);
    push(@found, @found2);
    close(S);

    if (@found != 0) {
      my @fields = split(/:/, $found[0]);
      return $fields[0];
    }

    return;
}

sub remove_actives() {
    my $ip = shift;

    open(S, ">>$lockfile2");
    flock(S, LOCK_EX);
    open(FILE, "<$actives");
    my @data = <FILE>;
    close(FILE);

    my @data_new = grep(!/$ip/, @data);

    open(FILE, ">$actives");
    print FILE join("\n", @data_new);
    close(FILE);
    close(S);
}

sub remove_actives_by_time() {
    my $time = shift;

    open(S, ">>$lockfile2");
    flock(S, LOCK_EX);
    open(FILE, "<$actives");
    my @data = <FILE>;
    close(FILE);

    my @data_new;

    my $line;
    while ($line = shift @data) {
      chomp($line);
      my @fields = split(/:/, $line);
      my $time2 = $fields[1];
      if ($time >= $time2) {
        # nicht mehr hinzufgen
        &log("remove_actives_by_time: $fields[0]");
      } else {
        push(@data_new, $line);
      }
    }

    open(FILE, ">$actives");
    print FILE join("\n",@data_new);
    close(FILE);

    close(S);
}

sub handler {
  my $sig = shift;
  &log("Caught SIG$sig...");
  &kill_proxymanager();
  exit 0;
}

$SIG{'TERM'} = \&handler;

########################
##### MAIN PROGRAM #####
########################

# Argumente prfen
if (@ARGV < 1) {
    &usage();
}

if ($ARGV[0] eq "-h") {
    &usage();
}



if ($ARGV[0] eq "-d") {
    die "Instance of proxy-manager already running! see $pidfile" if (&check_daemon() == 0);
    &open_proxy();
    &start_daemon();

} elsif ($ARGV[0] eq "-o") {
    die "missing arguments! try \"$0 -h\"" if (@ARGV != 4);

    if (&check_daemon() == 1) {
        die "no instance of proxy-manager running! see \"$0 -h\"";
    }
    &open_connection($ARGV[1], $ARGV[2], $ARGV[3]);

} elsif ($ARGV[0] eq "-c") {
    die "missing arguments! try \"$0 -h\"" if (@ARGV != 2);

    if (&check_daemon() == 1) {
        die "no instance of proxy-manager running! see \"$0 -h\"";
    }
    &close_connection($ARGV[1]);

} elsif ($ARGV[0] eq "-a") {
    die "missing arguments! try \"$0 -h\"" if (@ARGV != 2);

    if (&check_daemon() == 1) {
        die "no instance of proxy-manager running! see \"$0 -h\"";
    }
    if (my $ret = &active_connection($ARGV[1])) {
        print $ret;
    }

} elsif ($ARGV[0] eq "-k") {
    if (&check_daemon() == 1) {
        die "no instance of proxy-manager running! see \"$0 -h\"";
    }

    # sending SIGTERM(15)
    my $opid = `cat $pidfile`;
    kill 15, $opid;
    
    #&kill_proxymanager();

} elsif ($ARGV[0] eq "-t") {
    if (&check_daemon() == 0) {
        print "Der proxy-manager luft...\n";
        exit 0;
    } else {
        print "Der proxy-manager luft nicht...\n";
        exit 1;
    }

} else {
    &usage();
}

exit;



