package LISM::Handler::Script;

use strict;
use base qw(LISM::Handler);
use Data::Dumper;

=head1 NAME

LISM::Handler::Script - Handler to do script

=head1 DESCRIPTION

This class implements the L<LISM::Hanlder> interface to do script.

=head1 METHODS

=head2 getOrder

Get order to do handler.

=cut

sub getOrder
{
    return 'last';
}

=pod

=head2 pre_modify($dnp, $listp)

Do script before modify operation is done.

=cut

sub pre_modify
{
    my $self = shift;
    my ($dnp, $listp) = @_;
    my $conf = $self->{_config};

    return $self->_do_modify('pre', $dnp, $listp);
}

=head2 post_modify($dnp, $listp)

Do script after modify operation is done.

=cut

sub post_modify
{
    my $self = shift;
    my ($dnp, $listp) = @_;
    my $conf = $self->{_config};

    return $self->_do_modify('post', $dnp, $listp);
}

=pod

=head2 pre_add($dnp, $entryStrp)

Do script before add operation is done.

=cut

sub pre_add
{
    my $self = shift;
    my ($dnp, $entryStrp) = @_;
    my $conf = $self->{_config};

    return $self->_do_add('pre', $dnp, $entryStrp);
}

=head2 post_add($dnp, $entryStrp)

Do script after add operation is done.

=cut

sub post_add
{
    my $self = shift;
    my ($dnp, $entryStrp) = @_;
    my $conf = $self->{_config};

    return $self->_do_add('post', $dnp, $entryStrp);
}

=pod

=head2 pre_delete($dnp)

Do script before delete operation is done.
    
=cut
    
sub pre_delete
{
    my $self = shift;
    my ($dnp) = @_;
    my $conf = $self->{_config};

    return $self->_do_delete('pre', $dnp);
}

=head2 post_delete($dnp)

Do script after delete operation is done.

=cut

sub post_delete
{
    my $self = shift;
    my ($dnp) = @_;
    my $conf = $self->{_config};

    return $self->_do_delete('post', $dnp);
}

sub _checkConfig
{
    my $self = shift;
    my $conf = $self->{_config};
    my $rc = 0;

    if ($rc = $self->SUPER::_checkConfig()) {
        return $rc;
    }

    # check handler type
    foreach my $rule (@{$conf->{execrule}}) {
        if (!defined($rule->{type}) || $rule->{type} !~ /^(pre|post)$/) {
            $self->log(level => 'alert', message => "script handler type is invalid value");
             return 1;
        }
    }

    return 0;
}

=pod

=head2 _do_modify($type, $dnp, $listp)

Do script when modify operation is done.

=cut

sub _do_modify
{
    my $self = shift;
    my ($type, $dnp, $listp) = @_;
    my $conf = $self->{_config};
    my $dn = ${$dnp};
    my $rc = 0;
    my $match = 0;

    my ($rdn_val) = ($dn =~ /^[^=]+=([^,]+),/);

    foreach my $rule (@{$conf->{execrule}}) {
        if ($type ne $rule->{type}) {
            next;
        }

        # check the dn
        if (defined($rule->{dn}) && $dn !~ /$rule->{dn}/i) {
            next;
        }

        my @info;
        my @list = @{$listp};
        while (@list > 0) {
            my $action = shift @list;
            my $attr = lc(shift @list);
            my @values;

            while (@list > 0 && $list[0] ne "ADD" && $list[0] ne "DELETE" && $list[0] ne "REPLACE") {
                push(@values, shift @list);
            }

            if ($attr eq 'entrycsn') {
                last;
            }

            if (defined($rule->{match}) && !$match) {
                if ("$action: $attr" =~ /$rule->{match}/i) {
                    $match = 1;
                }

                foreach my $value (@values) {
                    if ("$attr: $value" =~ /$rule->{match}/i) {
                        $match = 1;
                        last;
                    }
                }
            }

            if ($action eq 'ADD') {
                $action = 'A';
            } elsif ($action eq 'DELETE') {
                $action = 'D';
            } elsif ($action eq 'REPLACE') {
                $action = 'R';
            }
            push(@info, "$action:$attr=".join('+', @values));
        }

        # check the rule
        if (defined($rule->{match}) && !$match) {
            next;
        }

        my $modinfo = "dn=$dn#".join('#', @info);
        foreach my $script (@{$rule->{op}{modify}->{script}}) {
            my $cmd = $script;
            $cmd =~ s/%r/$rdn_val/g;
            $cmd =~ s/%i/$modinfo/g;

            if ($^O ne 'MSWin32') {
                open(CMD, "$cmd; echo status=\$?|");
                while (<CMD>) {
                    $rc = $_;
                }
                ($rc) = ($rc =~ /status=([0-9]+)/);
                close(CMD);

                if ($rc) {
                    $self->log(level => 'err', message => "Script $script($dn) in modify failed($rc)");
                    if ($rc != 1) {
                        return $rc;
                    }
                } else {
                    $self->log(level => 'info', message => "Script $script($dn) in modify succeeded");
                }
            } else {
                system($cmd);
            }
        }
    }

    if ($rc == 1) {
        $rc = 0;
    }

    return $rc;
}

=pod

=head2 _do_add($type, $dnp, $entryStrp)

Do script when add opeartion is done.

=cut

sub _do_add
{
    my $self = shift;
    my ($type, $dnp,  $entryStrp) = @_;
    my $conf = $self->{_config};
    my $dn = ${$dnp};
    my $entryStr = ${$entryStrp}[0];
    my $rc = 0;

    my ($rdn_val) = ($dn =~ /^[^=]+=([^,]+),/);

    my @info;
    my (@line) = split(/\n/, $entryStr);
    while (@line > 0) {
        my ($attr, $values) = split(/: /, shift(@line));
        if ($attr =~ /^structuralobjectclass$/i) {
            last;
        }

        while ($line[0] =~ /^$attr: /) {
            $line[0] =~ s/^$attr: //;
            $values = "$values+$line[0]";
            shift @line;
        }
        push(@info, "$attr=$values");
    }
    my $addinfo = "dn=$dn#".join('#', @info);

    foreach my $rule (@{$conf->{execrule}}) {
        if ($type ne $rule->{type}) {
            next;
        }

        # check the dn
        if (defined($rule->{dn}) && $dn !~ /$rule->{dn}/i) {
            next;
        }

        # check the rule
        if (defined($rule->{match}) && $entryStr !~ /$rule->{match}/i) {
            next;
        }

        foreach my $script (@{$rule->{op}{add}->{script}}) {
            my $cmd = $script;
            $cmd =~ s/%r/$rdn_val/g;
            $cmd =~ s/%i/$addinfo/g;

            if ($^O ne 'MSWin32') {
                open(CMD, "$cmd; echo status=\$?|");
                while (<CMD>) {
                    $rc = $_;
                }
                ($rc) = ($rc =~ /status=([0-9]+)/);
                close(CMD);

                if ($rc) {
                    $self->log(level => 'err', message => "Script $script($dn) in add failed");
                    if ($rc != 1) {
                        return $rc;
                    }
                } else {
                    $self->log(level => 'info', message => "Script $script($dn) in add succeeded");
                }
            } else {
                system($cmd);
            }
        }
    }

    if ($rc == 1) {
        $rc = 0;
    }

    return $rc;
}

=pod

=head2 _do_delete($type, $dnp)

Do script when delete operation is done.

=cut

sub _do_delete
{
    my $self = shift;
    my ($type, $dnp) = @_;
    my $conf = $self->{_config};
    my $dn = ${$dnp};
    my $rc = 0;

    my ($rdn_val) = ($dn =~ /^[^=]+=([^,]+),/);

    my $info = "dn=$dn";
    foreach my $rule (@{$conf->{execrule}}) {
        if ($type !~ /$rule->{type}/i) {
            next;
        }

        if (defined($rule->{dn}) && $dn !~ /$rule->{dn}/i) {
            next;
        }

        foreach my $op (keys %{$rule->{op}}) {
            if ($op ne 'delete') {
                next;
            }

            foreach my $script (@{$rule->{op}{$op}->{script}}) {
                my $cmd = $script;
                $cmd =~ s/%r/$rdn_val/g;
                $cmd =~ s/%i/$info/g;

                if ($^O ne 'MSWin32') {
                    open(CMD, "$cmd; echo status=\$?|");
                    while (<CMD>) {
                        $rc = $_;
                    }
                    ($rc) = ($rc =~ /status=([0-9]+)/);
                    close(CMD);

                    if ($rc) {
                        $self->log(level => 'err', message => "Script $script($dn) in deleting failed");
                        if ($rc != 1) {
                            return $rc;
                        }
                    } else {
                        $self->log(level => 'info', message => "Script $script($dn) in deleting succeeded");
                    }
                } else {
                    system($cmd);
                }
            }
            next;
        }
    }

    if ($rc == 1) {
        $rc = 0;
    }

    return $rc;
}

=head1 SEE ALSO

L<LISM>,
L<LISM::Handler>

=head1 AUTHOR

Kaoru Sekiguchi, <sekiguchi.kaoru@secioss.co.jp>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2006 by Kaoru Sekiguchi

This library is free software; you can redistribute it and/or modify
it under the GNU LGPL.

=cut

1;
