#!/usr/bin/perl
# Copyright (C) 2012 eBox Technologies S.L.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

use strict;
use warnings;

use EBox;
use EBox::Global;
use EBox::Sudo;
use EBox::Backup;
use File::Slurp;
use Net::LDAP::LDIF;
use Error qw(:try);
use EBox::Util::Random;

use constant PASSWD_LEN => 10;

# Limitations:
#  1- only works with backups from master servers, not from slaves

EBox::init();

my ($bakFile, $output) = @ARGV;
defined $bakFile or
    _error("Usage:\n\t$0 BACKUP_FILE [OUTPUT_FILE]\n");

my $bakDir = EBox::Backup->_unpackAndVerify($bakFile);
try {
    EBox::Backup->_unpackModulesRestoreData($bakDir);
    my $ldif = _ldif($bakDir);
    _dumpUsers($ldif, $output);
} finally {
    EBox::Sudo::root("rm -rf $bakDir")
};

sub _ldif
{
    my ($bakDir) = @_;
    my $path = "$bakDir/eboxbackup/users.bak/master-data.ldif";
    if (not EBox::Sudo::fileTest('-r', $path)) {
        _error("Cannot read file LDIF file $path\n Are yo usure users module is in the backup?");
    }
    my $pathCopy = "$bakDir/tmp.lidf";
    EBox::Sudo::root("cp '$path' '$pathCopy'");
    EBox::Sudo::root("chown ebox.ebox '$pathCopy'");
    my $ldif = Net::LDAP::LDIF->new($pathCopy, 'r', 'onerror' => 'die');
    return $ldif;
}

sub _dumpUsers
{
    my ($ldif, $output) = @_;
    my @data;

    my $baseDN = _getBaseDN($ldif);
    if (not $baseDN) {
        _error("Cannot found base DN in the LDIF from the backup");
    }
    my $userClass = 'posixAccount';
    my $userDN = "ou=Users,$baseDN";;
    my $userDNRe = qr{$userDN$};
    my $groupClass = 'posixGroup';
    my $groupDN = "ou=Groups,$baseDN";;
    my $groupDNRe = qr{$groupDN$};

    while (my $entry = $ldif->read_entry()) {
        my $type;
        foreach my $class ($entry->get_value('objectClass')) {
            if ($class eq $userClass) {
                if ($entry->dn() =~ $userDNRe ) {
                    $type ='user';
                }
                last;
            } elsif ($class eq $groupClass) {
                if ($entry->dn() =~ $groupDNRe ) {
                    $type = 'group';
                }
                last;
            }
        }

        if (not defined $type) {
            next;
        } elsif ($type eq 'user') {
            my $userString = _userString($entry);
                if ($userString) {
                    unshift @data, $userString . "\n";
                }
        } elsif ($type eq 'group') {
            my $groupString = _groupString($entry);
            if ($groupString) {
                push @data, $groupString . "\n";
            }
        }
    }

    if ($output) {
        File::Slurp::write_file($output, \@data);
    } else {
        print @data;
    }
}

sub _userString
{
    my ($entry) = @_;

    my $user     = $entry->get_value('uid');
    $user or return undef;
    my $fullName = $entry->get_value('cn');
    my $surname  = $entry->get_value('sn');
    my $givenName    = $entry->get_value('givenName');
    my $description    = $entry->get_value('description');

    my $quota    = $entry->get_value('quota');
    my $password = EBox::Util::Random::generate(PASSWD_LEN); # guaranteed to not
                                                             # be ','
    return join(',',
                'user',
                $user,
                $password,
                $fullName,
                $surname,
                $givenName,
                $description,
                $quota
               );
}

sub _groupString
{
    my ($entry) = @_;
    my $group = $entry->get_value('cn');
    if (not $group) {
        return undef;
    } elsif ($group eq '__USERS__') {
        #dont import all user groups
        return undef;
    }

    my $description = $entry->get_value('description');
    my @users = $entry->get_value('memberUid');
    my $userString = join ':', @users;
    return join(',', 'group', $group, $description, $userString);
}

sub _getBaseDN
{
    my ($ldif) = @_;
    # get DN from first entry, not elegant but seems to work with our ldifs
    my $entry = $ldif->read_entry();
    $entry or return undef;

    return $entry->dn();
}

sub _error
{
    my ($err) = @_;

    print "$err\n";
    exit 1;
}

1;