package access;


my %acl_ent = (
    "none" =>		0x00000000,
    "all" => 		0xFFFFFFFF,
    "authenticate" => 	0x00000001,
    "status" =>		0x00000002,
    "grouping" =>	0x00000004,
    "page" =>		0x00000008,
    "archiving" =>	0x00000010,
    "alarm_acking" =>	0x00000020,
    "perf" =>		0x00000040,
    "remove" =>		0x00000080,
    "event" =>		0x00000100
);


use strict;

sub new
{
    my ($this, @files) = @_;
    my  $class = ref($this) || $this;
    my  $self = {};

    bless($self, $class);
    $self->init;
    $self->{"files"} = [];
    my $file;
    foreach $file (@files) {
	$self->add_file( $file );
    }
    return $self;
}


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

    $self->{"perms"} = [];
}


sub add_file {
    my( $self, $file ) = @_;

    push( @{$self->{"files"}}, {
	"file" => $file,
	"mtime" => 0
    } );
}


sub check_files {
    my( $self ) = @_;
    my( $file, $ref, $reread );

    foreach $ref (@{$self->{"files"}}) {
	my $mtime = ((stat($ref->{"file"}))[9]);
	if( ( $mtime && ($ref->{"mtime"} != $mtime) )
	    || ( $ref->{"mtime"} && ! $mtime ) ) {
	    $reread = 1;
	    last;
	}
    }
    return unless( $reread );

    $self->init();

    foreach $ref (@{$self->{"files"}}) {
	my $mtime = ((stat($ref->{"file"}))[9]);
	open( IN, "<".($ref->{"file"}) );
	while( <IN> ) {
	    chomp;
	    next if( /^#/ || /^[\s\t]*$/ );
	    $self->add_acl( $_, $ref->{"file"}." ($.)" );
	}
	close IN;
	$ref->{"mtime"} = $mtime;
    }
}


sub add_acl {
    my( $self, $acl, $context ) = @_;

    if( $acl =~ /^(.)([^\s\t]*)$/ ) {
	# old style
	my( $allow, $host ) = ($1,$2);
	unless( $allow =~ /[+-]/ ) {
	    $host = $allow.$host;
	    $allow = "+";
	}
	$host = $self->host_normalize( $host );
	push( @{$self->{"perms"}}, {
	    "rule" => $host,
	    "perms" => [
		[ ($allow eq "+")?($acl_ent{"all"}):($acl_ent{"none"}),
	    	  ($allow eq "-")?($acl_ent{"all"}):($acl_ent{"none"})
		]],
	    "continue" => 0
	} );
    }
    elsif( $acl =~ /^(.*)=>(.*)/ ) {
	my( $rule, $acl ) = ($1,$2);
	my( $allow, $deny ) = (0,0);
	my @list;
	
	foreach $acl (split( /[\s\t]+/, $acl )) {
	    next unless( $acl );
	    my( $add ) = substr( $acl, 0, 1 );
	    $acl = substr( $acl, 1 );
	    my @ent = grep( $_ eq $acl, keys %acl_ent );
	    unless( @ent && ($add =~ /[+-]/) ) {
		BigSister::common::log( "warning", "ignoring invalid acl entry $add$acl - $context" );
		next;
	    }
	    $acl = $acl_ent{$ent[0]};
	    if( $add eq "+" ) {
		push( @list, [ $acl, 0 ] );
	    }
	    else {
		push( @list, [ 0, $acl ] );
	    }
	}

	$rule = join( " ", grep( $_, split( /[\s\t]+/, $rule ) ) );
	if( $rule =~ /^host (.*)$/ ) {
	    $rule = $self->host_normalize( $1 );
	}
	unless( grep( $rule =~ /^$_$/,
		"anonymous",
		"user .*",
		"ip .*",
		"member .*",
		"name .*" ) ) {
	    BigSister::common::log( "warning", "ignoring invalid permissions rule - $context" );
	}
	print "  access::add_acl adding: $rule\n" if( $BigSister::common::dl>5 );
	push( @{$self->{"perms"}}, {
	    "rule" => $rule,
	    "perms" => \@list,
	    "continue" => 1
	} );
    }
    else {
	BigSister::common::log( "warning", "ignoring invalid permissions entry - $context" );
    }
}



sub get_perms {
    my( $self, @context ) = @_;
    my $rule;

    print "access::get_perms( ".join(" ",@context)." )\n" if( $BigSister::common::dl>5 );
    my $perm = 0;
    unless( @{$self->{"perms"}} ) {
	$perm = $acl_ent{"all"};
    }
    foreach $rule (@{$self->{"perms"}}) {
	my $pattern = $rule->{"rule"};
	if( grep( $_ =~ /^$pattern/, @context ) ) {
	    print "  matched $pattern\n" if( $BigSister::common::dl>6 );
	    my $list = $rule->{"perms"};
	    my $i;
	    foreach $i (@$list) {
		$perm &= ~($i->[1]);
		$perm |= $i->[0];
	    }
	    last unless( $rule->{"continue"} );
	}
    }
    if( $BigSister::common::dl>3 ) {
	print "access: ";
	my( $text, $mask );
	while( ($text,$mask) = each %acl_ent ) {
	    print $text." " if( $mask && (($perm & $mask) == $mask) );
	}
	print "\n";
    }
    return $perm;
}



sub is_allowed {
    my( $self, $perms, $what ) = @_;

    return(0) unless( $acl_ent{$what} );
    return( $perms & $acl_ent{$what} );
}



sub host_normalize {
    my( $self, $host ) = @_;

    if( $host =~ /^[0-9\.\*]+$/ ) {
	while( $host =~ /\.0/ ) {
	    $host = $`.".".$';
	}
	$host = "ip $host";
    }
    else {
	$host = "name $host";
    }
    return $host;
}


1;
