#!/usr/bin/perl
#
# ID synchronization command
#
# Copyright(c) SECIOSS CORPORATION 2010
#

use strict;
use lib '/opt/secioss/lib/perl';
use LISM;
use XML::Simple;
use Net::LDAP::Util qw(ldap_error_desc);
use Net::LDAP::Constant qw(:all);
use DBI;
use DBD::SQLite;
use Encode;
use POSIX;
use Getopt::Std;
use Data::Dumper;

our $BASEDIR = '/opt/secioss';
our $CONFDIR = "$BASEDIR/etc";
our $BASEDN = 'o=secioss';
our $PASSWD = 'secioss';

my %opt;
getopts("f:d:rn", \%opt);

if (@ARGV < 1) {
    usage();
}

our $SYNCDIR = "$BASEDIR/var/lib/ldap";
if (defined($opt{d})) {
    $SYNCDIR = $opt{d};
}
our $DBFILE = "$BASEDIR/var/lib/ldap/user.db";
our $STATUSFILE = "$SYNCDIR/status";

sub updatesync
{
    my ($lism, $type, $data, $syncfilter) = @_;
    my @modinfo;
    my $rdn;

    if ($type eq 'master') {
        $rdn = 'cn=master-sync';
    } else {
        $rdn = 'cn=cluster-sync';
        push(@modinfo, 'DELETE', 'lismSyncErrNode', $data);
    }

    push(@modinfo, 'REPLACE', 'lismSyncStatus', 'sync');
    if ($syncfilter) {
        push(@modinfo, 'REPLACE', 'lismSyncFilter', $syncfilter);
    }

    my $rc = $lism->modify("$rdn,$BASEDN", @modinfo);
    if ($rc) {
        print STDERR ldap_error_desc($rc)."($rc)\n";
    }

    return $rc;
}

sub readsync
{
    my ($lism, $type, $data, $syncfilter) = @_;
    my $rdn;
    my $filter = '';

    if ($type eq 'master') {
        $rdn = 'cn=master-sync';
    } else {
        $rdn = 'cn=cluster-sync';
        $filter = "(lismSyncErrNode=$data)";
    }

    if ($syncfilter) {
        $syncfilter =~ s/\(/\\28/g;
        $syncfilter =~ s/\)/\\29/g;
        if ($filter) {
            $filter = "(&$filter(lismSyncFilter=$syncfilter))";
        } else {
            $filter = "(lismSyncFilter=$syncfilter)";
        }
    }
    if (!$filter) {
        $filter = '(objectClass=*)';
    }

    my ($rc, $entry) = $lism->search("$rdn,$BASEDN", 0, 1, 0, 0, $filter);
    if ($rc) {
        print STDERR ldap_error_desc($rc)."($rc)\n";
    } else {
        my @messages = ($entry =~ /^lismSyncErrMessage: (.+)$/gmi);
        if (@messages) {
            foreach my $message (@messages) {
                print encode('utf8', $message."\n");
            }
        }
    }

    return $rc;
}

sub factory
{
    my ($conf) = @_;

    my $lism = new LISM;
    $lism->config('basedn', $BASEDN);
    $lism->config('admindn', "cn=Idsync,$BASEDN");
    $lism->config('adminpw', $PASSWD);
    $lism->config('syncdir', $SYNCDIR);
    $lism->config('conf', $conf);
    if ($^O eq 'MSWin32') {
        $lism->config('logfile', "/opt/secioss/var/log/lism.log");
    }

    return $lism;
}

sub usage
{
    print STDERR "Usage: idsync [-f conf] [-r] [-n] idp|sp\n";
    exit 1;
}

my $list = "$BASEDIR/var/lib/csv/user.csv";
my $type = $ARGV[0];
if ($type ne 'idp' && $type ne 'sp') {
    usage();
}
my $rc = 0;

if ($type eq 'sp' && -f $list) {
    print STDERR "Delete $list\n";
    unlink($list);
}

my $lism = factory("$CONFDIR/lism-$type.conf");
$rc = $lism->init();
if ($rc) {
    print STDERR "Bad configuration\n";
    exit 1;
}
$lism->bind("cn=Idsync,$BASEDN", $PASSWD);

if ($type eq 'idp') {
    if (defined($opt{r})) {
        $rc = readsync($lism, 'cluster', 'IDP');
    } else {
        $rc = updatesync($lism, 'cluster', 'IDP');
    }
    if ($rc) {
        print STDERR "Can't get updated entries\n";
        exit 1;
    }
} else {
    my $prevtime;
    if (-f $STATUSFILE) {
        if (!open(STATUS, "<$STATUSFILE")) {
            print STDERR "Can't open status file\n";
            exit 1;
        }
        $prevtime = <STATUS>;
        $prevtime =~ s/\n//;
        close(STATUS);
    }
    if (!$prevtime) {
        $prevtime = '19700101000000';
    }

    my $ctime = strftime("%Y%m%d%H%M%S", localtime(time));
    $rc = updatesync($lism, 'master', 'SP', '(seciossPersonModifyTime>='.$prevtime.'Z)');
    if ($rc) {
        print STDERR "Can't get updated entries\n";
        exit 1;
    }

    if (!-f $list) {
        print STDERR "$list doesn't exist\n";
        exit 1;
    }

    if (defined($opt{n})) {
        print "Updated user list is created\n";
        exit 0;
    }

    my $file;
    if (defined($opt{f})) {
        $file = $opt{f};
    } else {
        $file = "$CONFDIR/lism.conf";
    }
    my $conf = XMLin("$CONFDIR/lism-sp.conf", ForceArray => 1);
    my $appconf = XMLin($file, ForceArray => 1);
    foreach my $key (keys %{$appconf->{sync}[0]->{data}}) {
        $conf->{sync}[0]->{data}{$key} = $appconf->{sync}[0]->{data}{$key};
    }
    foreach my $key (keys %{$appconf->{data}}) {
        $conf->{data}{$key} = $appconf->{data}{$key};
    }

    my $fh;
    open($fh, ">$CONFDIR/lism-all.conf");
    XMLout($conf, RootName => 'config', OutputFile => $fh);
    close $fh;

    $lism = factory("$CONFDIR/lism-all.conf");
    $rc = $lism->init();
    if ($rc) {
        print STDERR "Bad configuration\n";
        exit 1;
    }
    $lism->bind("cn=Idsync,$BASEDN", $PASSWD);

    if (!open(LIST, "<$list")) {
        print STDERR "Can't read $list\n";
        exit 1;
    }
    if (defined($opt{r})) {
        print "Differentail data:\n";
    }
    while (<LIST>) {
        my ($user) = split(/, */);
        if (defined($opt{r})) {
            $rc = readsync($lism, 'cluster', 'SP', "(uid=$user)");
        } else {
            $rc = updatesync($lism, 'cluster', 'SP', "(uid=$user)");
        }
        if ($rc) {
            print STDERR "Synchronization of $user failed\n";
            last;
        } elsif (!defined($opt{r})) {
            print "Synchronization of $user succeded\n";
        }
    }
    close(LIST);
    unlink($list);

    if (!$rc && !defined($opt{r})) {
        if (!open(STATUS, ">$STATUSFILE")) {
            print STDERR "Can't open status file\n";
            exit 1;
        }
        print STATUS "$ctime";
        close(STATUS);
    }
}

exit ($rc);
