# tcpwrappers-lib.pl
# Library for TCP Wrappers

BEGIN { push(@INC, ".."); };
use WebminCore;
&init_config();

# list_rules($filename)
# Parse rules from /etc/hosts.*
# File format described in "man 5 hosts_access"
sub list_rules {
    my $file = shift;
    my @ret;
    my $id = 0;

    open(HOSTS, "<".$file) || return ();
    my $line;
    my $last_line = '';
    my $lnum = 0;
    while ($line = <HOSTS>) {
	my ($slnum, $elnum) = ($lnum, $lnum);
	s/\r|\n//g;

	while ($line =~ /^(.*)\\/) {
	    # Continuation line! Read the next one and append it
	    local $before = $1;
	    local $nxt = <HOSTS>;
	    $nxt =~ s/\r|\n//g;
	    $line = $before.$nxt;
	    $elnum++; $lnum++;
        }

	if ($line =~ /^\#(.*)/) {
	    # Comment
	    $cmt = $cmt ? $cmt."\n".$1 : $1;
	} elsif ($line =~ /^\s*$/) {
	    $cmt = undef;
	} else {
	    my @cmtlines = split(/\n/, $cmt);
	    $cmt = undef;

        # Fix further splitting on : to work with ipv6
        my $ipv6;
        my $ipv6_enc;

        # Match ipv6 with or without range
        if ($line =~ /(?|(\[[:\da-f]+\]\/\d+)|(\[[:\da-f]+\]))/i) {
            $ipv6 = $1;
            $ipv6_enc = &encode_base64($ipv6);
            $line =~ s/\Q$ipv6\E/$ipv6_enc/;
        }
        my ($service, $host, $cmd) = split /:/, $line, 3;
	    $service =~ s/^\s*//; $service =~ s/\s*$//;
	    $host =~ s/^\s*\Q$ipv6_enc\E/$ipv6/; $host =~ s/\s*$//;
	    
	    push @ret, { 'id' => $id++,
			 'service' => $service,
			 'host' => $host,
			 'cmd' => $cmd,
			 'line' => $slnum-scalar(@cmtlines),
			 'eline' => $elnum
			 };
	}
	$lnum++;
    }
    close FILE;

    return @ret;
}

# list_services()
# List system services from (x)inetd or return ()
sub list_services {
    my @ret;

    if (&foreign_installed("xinetd")) {
	&foreign_require("xinetd", "xinetd-lib.pl");
	my @conf = &foreign_call('xinetd', 'get_xinetd_config');
	foreach $x (@conf) {
	    next if ($x->{'quick'}{'server'}[0] !~ /\/([^\/]+)$/);
	    push @ret, $1;
	}
    } elsif (&foreign_installed("inetd")) {
	&foreign_require("inetd", "inetd-lib.pl");
	my @conf = &foreign_call('inetd', 'list_inets');
	foreach $x (@conf) {
	    next unless ($x->[9] =~ /^(\S+)/);
	    push @ret, $1;
	}
    }

    return &unique(@ret);
}

# delete_rule($filename, &rule)
# Removes one rule entry from the file
sub delete_rule {
    my ($filename, $rule) = @_;

    my $lref = &read_file_lines($filename);
    my $len = $rule->{'eline'} - $rule->{'line'} + 1;
    splice(@$lref, $rule->{'line'}, $len);
    &flush_file_lines($filename);
}

# create_rule($filename, &rule)
# Adds new rule
sub create_rule {
    my ($file, $rule) = @_;

    my $lref = &read_file_lines($file);
    my $newline = $rule->{'service'}.' : '.$rule->{'host'}.($rule->{'cmd'} ? ' : '.$rule->{'cmd'} : '');
    push(@$lref, $newline);
    &flush_file_lines($file);
}

# modify_rule($filename, &old_rule, &new_rule)
# Updates rule
sub modify_rule {
    my ($filename, $oldrule, $newrule) = @_;

    my @newline = ($newrule->{'service'}.' : '.$newrule->{'host'}.($newrule->{'cmd'} ? ' : '.$newrule->{'cmd'} : ''));

    my $lref = &read_file_lines($filename);
    my $len = $oldrule->{'eline'} - $oldrule->{'line'} + 1;
    splice(@$lref, $oldrule->{'line'}, $len, @newline);
    &flush_file_lines($filename);
}

1;
