#!/usr/bin/perl
#
# SOAP Interface of LISM
#
# This code was developped by SECIOSS (http://www.secioss.co.jp/).
#
#              Copyright (C) 2009 SECIOSS CORPORATION
#
#  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.
#

package SoapLism;

use strict;
use LISM;
use Net::LDAP::Util qw(ldap_error_text);
use CGI::Session;
use Config::General;
use MIME::Base64;
use Encode;
use Data::Dumper;

our $CONF = 'lismapi.conf';
my $SESSION_DIR = '/tmp';

sub _config
{
    my $config = Config::General->new($CONF);
    my %conf = $config->getall;

    if (!defined($conf{'basedn'})) {
        $conf{'basedn'} = 'o=lism,o=cgi';
    }
    if (!defined($conf{'sessionexpire'})) {
        $conf{'sessionexpire'} = 86400;
    }

    return %conf;
}

sub _checksession
{
    my $self = shift;
    my ($session, %conf) = @_;

    if ($session->id) {
        if ($session->ctime + $conf{'sessionexpire'} < time) {
            $session->close();
            $session->delete();
            return 0;
        }
        return 1;
    } else {
        return 0;
    }
}

sub _factory
{
    my $self = shift;
    my (%conf) = @_;

    my $lism = new LISM;
    $lism->config('basedn', $conf{'basedn'});
    if (defined($conf{'admin'})) {
        $lism->config('admindn', "cn=$conf{'admin'},$conf{'basedn'}");
    }
    foreach my $key (qw(syncdir conf adminpw)) {
        $lism->config($key, $conf{$key});
    }

    if ($lism->init()) {
        return undef;
    }

    return $lism;
}

sub isauth
{
    my $self = shift;
    my ($sessid) = @_;
    my %conf = $self->_config;

    my $session = CGI::Session->load($sessid);
    return $self->_checksession($session, %conf);
}

sub bind
{
    my $self = shift;
    my ($binddn, $bindpw) = @_;
    my %conf = $self->_config;

    my $lism = $self->_factory(%conf);
    if (!defined($lism)) {
        return [-1, "Can't load LISM"];
    }

    $binddn =~ s/o=lism$/$conf{'basedn'}/i;

    my $rc = $lism->bind($binddn, $bindpw);
    if (!$rc) {
        my $session = CGI::Session->new(undef, undef, {Directory => $SESSION_DIR});
        $session->param('binddn', $binddn);
        $session->param('bindpw', $bindpw);

        return [$rc, ldap_error_text($rc), $session->id];
    } else {
        return [$rc, ldap_error_text($rc), ''];
    }
}

sub unbind
{
    my $self = shift;
    my ($sessid) = @_;
    my %conf = $self->_config;

    my $session = CGI::Session->load($sessid);
    if (!$self->_checksession($session, %conf)) {
        return [-1, "Not authenticated"];
    }

    $session->close();
    $session->delete();    
}

sub search
{
    my $self = shift;
    my ($sessid, $base, $scope, $deref, $sizeLim, $timeLim, $filter, $attrOnly, $attrs) = @_;
    my @entries;
    my %conf = $self->_config;

    my $session = CGI::Session->load($sessid);
    if (!$self->_checksession($session, %conf)) {
        return [-1, "Not authenticated"];
    }

    my $lism = $self->_factory(%conf);
    if (!defined($lism)) {
        return [-1, "Can't load LISM"];
    }

    $base =~ s/o=lism$/$conf{'basedn'}/i;
    $filter =~ s/o=lism\)/$conf{'basedn'})/gi;
    $filter =~ s/&amp;/&/g;

    my $rc = $lism->bind($session->param('binddn'), $session->param('bindpw'));
    if ($rc) {
        return [$rc, ldap_error_text($rc)];
    }

    my @entries;
    ($rc, @entries) = $lism->search($base, $scope, $deref, $sizeLim, $timeLim, $filter, $attrOnly, @{$attrs});
    for (my $i = 0; $i < @entries; $i++) {
        $entries[$i] =~ s/$conf{'basedn'}$/o=lism/gmi;
    }

    return [$rc, ldap_error_text($rc), \@entries];
}

sub add
{
    my $self = shift;
    my ($sessid, $dn, $req) = @_;
    my $entryStr;
    my %conf = $self->_config;

    my $session = CGI::Session->load($sessid);
    if (!$self->_checksession($session, %conf)) {
        return [-1, "Not authenticated"];
    }

    $entryStr = "dn: $dn\n";
    foreach my $attr (keys %{$req}) {
        my @values = @{$req->{$attr}};
        $attr =~ s/_/;/g;
        for (my $i = 0; $i < @values; $i++) {
            $entryStr = "$entryStr$attr: $values[$i]\n";
        }
    }

    $entryStr =~ s/o=lism$/$conf{'basedn'}/gmi;

    my $lism = $self->_factory(%conf);
    if (!defined($lism)) {
        return [-1, "Can't load LISM"];
    }

    my $rc = $lism->bind($session->param('binddn'), $session->param('bindpw'));
    if ($rc) {
        return [$rc, ldap_error_text($rc)];
    }

    $rc = $lism->add($entryStr);

    return [$rc, ldap_error_text($rc)];
}

sub modify
{
    my $self = shift;
    my ($sessid, $dn, $req) = @_;
    my @changes = ();
    my %conf = $self->_config;

    my $session = CGI::Session->load($sessid);
    if (!$self->_checksession($session, %conf)) {
        return [-1, "Not authenticated"];
    }

    foreach my $action (keys %{$req}) {
        if (ref($req->{$action}) eq 'ARRAY') {
            foreach my $info (@{$req->{$action}}) {
                foreach my $attr (keys %{$info}) {
                    my @values;
                    foreach my $value (@{$info->{$attr}}) {
                        $value =~ s/o=lism$/$conf{'basedn'}/i;
                        push(@values, $value);
                    }
                    $attr =~ s/_/;/g;
                    push(@changes, uc($action), $attr, @values);
                }
            }
        } else {
            foreach my $attr (keys %{$req->{$action}}) {
                my @values;
                foreach my $value (@{$req->{$action}->{$attr}}) {
                    $value =~ s/o=lism$/$conf{'basedn'}/i;
                    push(@values, $value);
                }
                $attr =~ s/_/;/g;
                push(@changes, uc($action), $attr, @values);
            }
        }
    }

    my $lism = $self->_factory(%conf);
    if (!defined($lism)) {
        return [-1, "Can't load LISM"];
    }

    $dn =~ s/o=lism$/$conf{'basedn'}/i;

    my $rc = $lism->bind($session->param('binddn'), $session->param('bindpw'));
    if ($rc) {
        return [$rc, ldap_error_text($rc)];
    }

    $rc = $lism->modify($dn, @changes);

    return [$rc, ldap_error_text($rc)];
}

sub delete
{
    my $self = shift;
    my ($sessid, $dn) = @_;
    my @attrs = ();
    my %conf = $self->_config;

    my $session = CGI::Session->load($sessid);
    if (!$self->_checksession($session, %conf)) {
        return [-1, "Not authenticated"];
    }

    my $lism = $self->_factory(%conf);
    if (!defined($lism)) {
        return [-1, "Can't load LISM"];
    }

    $dn =~ s/o=lism$/$conf{'basedn'}/i;

    my $rc = $lism->bind($session->param('binddn'), $session->param('bindpw'));
    if ($rc) {
        return [$rc, ldap_error_text($rc)];
    }

    $rc = $lism->delete($dn);

    return [$rc, ldap_error_text($rc)];
}

1;
