#!/usr/bin/env perl
# BEGIN COPYRIGHT BLOCK
# 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; version 2 of the License.
#
# 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.
#
# In addition, as a special exception, Red Hat, Inc. gives You the additional
# right to link the code of this Program with code not covered under the GNU
# General Public License ("Non-GPL Code") and to distribute linked combinations
# including the two, subject to the limitations in this paragraph. Non-GPL Code
# permitted under this exception must only link to the code of this Program
# through those well defined interfaces identified in the file named EXCEPTION
# found in the source code files (the "Approved Interfaces"). The files of
# Non-GPL Code may instantiate templates or use macros or inline functions from
# the Approved Interfaces without causing the resulting work to be covered by
# the GNU General Public License. Only Red Hat, Inc. may make changes or
# additions to the list of Approved Interfaces. You must obey the GNU General
# Public License in all respects for all of the Program code and other code used
# in conjunction with the Program except the Non-GPL Code covered by this
# exception. If you modify this file, you may extend this exception to your
# version of the file, but you are not obligated to do so. If you do not wish to
# provide this exception without modification, you must delete this exception
# statement from your version and license this file solely under the GPL without
# exception.
#
#
# Copyright (C) 2007 Red Hat, Inc.
# All rights reserved.
# END COPYRIGHT BLOCK
#

use lib qw(/usr/lib/x86_64-linux-gnu/dirsrv/perl);

use strict;

use File::Basename;
use Net::Domain qw(hostfqdn);

# Admin Setup modules
use AdminUtil;
use AdminServer;

# Directory Setup modules
use Resource;
use Setup;
use SetupLog;
use DialogManager;
use Inf;
use DSUtil;

sub reg_get_passwd
{
    my $setup = shift;
    my $key = shift;
    my $value0 = shift;
    my $value1 = shift;

    print("\n==============================================================================\n");
    if ( $value1 )
    {
        $setup->msg(0, $key, $value0, $value1);
    }
    elsif ( $value0 )
    {
        $setup->msg(0, $key, $value0);
    }
    else
    {
        $setup->msg(0, $key);
    }
    system("stty -echo");
    my $ans = <STDIN>;
    system("stty echo");
    print "\n";
    chop($ans);
    return $ans;
}

sub reg_get_response
{
    my $setup = shift;
    my $key = shift;
    my $value = shift;

    print("\n==============================================================================\n");
    $setup->msg(0, $key, $value);
    my $ans = <STDIN>;
    print "\n";
    chop($ans);
    return $ans;
}

my $res = new Resource("/usr/share/dirsrv/properties/register-ds-admin.res",
                       "/usr/share/dirsrv/properties/setup-ds-admin.res",
                       "/usr/share/dirsrv/properties/setup-ds.res");

my $setup = new Setup($res);

$setup->msg('begin_ds_registration');
# get existing instances
my $instconfigdir = $setup->{configdir};
my %instances = ();
for my $dir (glob("$setup->{configdir}/slapd-*"))
{
    if (-d $dir)
    {
        my $dname = dirname($dir);
        my $bname = basename($dir);
        push @{$instances{$dname}}, $bname;
    }
}

# in case Directory Servers are installed at the unexpected location.
my $done = 0;
my $ans;
while ( !$done && ($ans =  reg_get_response($setup, 'subds_conf_prompt', "/etc/dirsrv")) )
{
    if ( $ans eq "" || !$ans )
    {
        $done = 1;
    }
    elsif ( ! -d $ans )
    {
        ;
    }
    elsif ( basename($ans) =~ /^slapd-/ )
    {
        my $dname = dirname($ans);
        my $bname = basename($ans);
        if ( exists $instances{$dname} )
        {
            my $addit = 1;
            foreach my $thisslapd ( @{$instances{$dname}} )
            {
                if ( $thisslapd eq $bname )
                {
                    $addit = 0;
                    goto out0;
                }
            }
out0:
            if ( $addit )
            {
                push @{$instances{$dname}}, $bname;
            }
        }
        else
        {
            push @{$instances{$dname}}, $bname;
        }
    }
    else
    {
        $ans =~ s/^\s+//;
        $ans =~ s/[\/\s]+$//;
        my $rc = opendir(DIR, $ans);
        if ( $rc )
        {
            my $file = "";
            while ( defined($file = readdir(DIR)) )
            {
                next if ( !("$file" =~ /^slapd-/) );
                if ( exists $instances{$ans} )
                {
                    my $addit = 1;
                    foreach my $thisslapd ( @{$instances{$ans}} )
                    {
                        if ( $thisslapd eq $file )
                        {
                            $addit = 0;
                            goto out1;
                        }
                    }
out1:
                    if ( $addit )
                    {
                        push @{$instances{$ans}}, $file;
                    }
                }
                else
                {
                    push @{$instances{$ans}}, $file;
                }
            }
            closedir(DIR);
        }
    }
}

my @instconfigdirs = keys %instances;
if ( $#instconfigdirs < 0 )
{
    $setup->msg($FATAL, 'error_no_ds');
    $setup->doExit(1);
}

print("\n==============================================================================\n");
$setup->msg('candidate_list_txt');
foreach my $c ( @instconfigdirs )
{
    foreach my $i ( @{$instances{$c}} )
    {
        print "    $c/$i\n";
    }
}

# see if there is already a configds
my $admConf = AdminUtil::getAdmConf("$instconfigdir/admin-serv");
my @admConfKeys = keys %$admConf;
my $orig_confdsid = "";
my $new_confdsid = "";
my $new_confdir = "";
my $adminuid = "";
my @errs = ();
my $fqdn = hostfqdn();

# set defaults
if ( $#admConfKeys >= 4 ) # admserv.conf, console.conf, httpd.conf, nss.conf
{
    # Admin Server is installed; that is Config DS exists, which may be
    # replaced with a new one in the RegDSDialogs
    $orig_confdsid = getLocalConfigDS("$instconfigdir/admin-serv");
    $setup->{inf}->{slapd}->{ServerIdentifier} = $orig_confdsid;
    $setup->{inf}->{slapd}->{config_dir} = $instconfigdir;
    $setup->{inf}->{slapd}->{Instances} = \%instances;
    $setup->{inf}->{General}->{ConfigDirectoryLdapURL} = $admConf->{ldapurl};
    $setup->{inf}->{General}->{ConfigDirectoryAdminID} = $admConf->{userdn};
    $setup->{inf}->{General}->{AdminDomain} = $admConf->{AdminDomain};
    $setup->{inf}->{General}->{SuiteSpotUserID} = $admConf->{SuiteSpotUserID};
    $setup->{inf}->{General}->{SuiteSpotGroup} = $admConf->{SuiteSpotGroup};
    ($setup->{inf}->{General}->{FullMachineName} = $admConf->{ldapurl}) =~
                                                   s/.*:\/\/(.*):[0-9]*\/.*/\1/;
    $setup->{inf}->{admin}->{SysUser} = $admConf->{sysuser};
    $adminuid = $admConf->{userdn};

    # read additional config from config DS
    my $pset = AdminUtil::getPset($admConf);
    if ($pset && %{$pset}) {
        $setup->{inf}->{admin}->{Port} = $pset->{"configuration.nsserverport"};
        $setup->{asorigport} = $pset->{"configuration.nsserverport"}; # save orig. port
        $setup->{inf}->{admin}->{ServerIpAddress} = $pset->{"configuration.nsserveraddress"};
    }
    my $admpw = AdminUtil::getAdmpw($admConf);
    if ($admpw && %{$admpw}) {
        $setup->{inf}->{admin}->{ServerAdminID} = $admpw->{ServerAdminID};
        $setup->{inf}->{admin}->{ServerAdminPwd} = $admpw->{ServerAdminPwd};
    }
    $setup->{reconfigas} = 1; # allow AS reconfig

    my $dialogmgr = new DialogManager($setup, $res, $TYPICAL);

    require RegDSDialogs;

    my @dialogs = RegDSDialogs->getDialogs();

    $dialogmgr->addDialog(@dialogs);

    my $rc = $dialogmgr->run();
    if ($rc)
    {
        $setup->doExit(1);
    }

    $new_confdsid = $setup->{inf}->{slapd}->{ServerIdentifier};
    $new_confdir = $setup->{inf}->{slapd}->{config_dir};
    my $newinst = "slapd-$new_confdsid";
    my $inf = createInfFromConfig("$instconfigdir/$newinst", $newinst);
    if ( ! $inf )
    {
        $setup->msg($FATAL, 'error_create_inf_from_config',
                                "$instconfigdir/$newinst");
        $setup->doExit(1);
    }

    if ( $orig_confdsid ne $new_confdsid )
    {
        # To switch to the new Config DS, unregister the old one
        print("\n==============================================================================\n");
        $setup->msg('unregister_old_confds', $orig_confdsid);
        # If we don't have it, prompt for the Admin password
        if (!$setup->{inf}->{General}->{ConfigDirectoryAdminPwd} ||
            "" eq $setup->{inf}->{General}->{ConfigDirectoryAdminPwd})
        {
            $ans = reg_get_passwd($setup, 'input_admin_passwd', $adminuid);
            $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} = $ans;
        }
        while (!unregisterDSWithConfigDS($orig_confdsid, \@errs, $setup->{inf}))
        {
            $setup->msg($FATAL, 'error_unregister_ds', $orig_confdsid);
            $ans = reg_get_passwd($setup, 'input_admin_passwd', $adminuid);
            $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} = $ans;
            @errs = ();
        }
        # updating the port number
        my $oldport = 0;
        my $newport = $inf->{slapd}->{ServerPort};
        ($oldport = $setup->{inf}->{General}->{ldapurl}) =~ s/.*:([0-9]*)\/.*/\1/;
        $setup->{inf}->{General}->{ldapurl} =~ s/$oldport/$newport/;
        $setup->{inf}->{General}->{ConfigDirectoryLdapURL} = "ldap://" . $fqdn . ":". $newport . "/o=NetscapeRoot";
    }
    # Set the new inf to $setup->{inf}
    $setup->{inf}->{slapd} = $inf->{slapd};
    $setup->{inf}->{slapd}->{config_dir} = $instconfigdir;
    $setup->{inf}->{slapd}->{Instances} = \%instances;
}
else
{
    # Admin Server is not set up.
    # %instances has more than one instance
    # note: this is orig_confdsid is just a candidate...
    my $orig_confdir = $instconfigdirs[0];
    my @orig_confdsids = @{$instances{$orig_confdir}};
    ($orig_confdsid = $orig_confdsids[0]) =~ s/slapd-(.*)/\1/;

    my $originst = "slapd-$orig_confdsid";
    my $inf = createInfFromConfig("$orig_confdir/$originst", $originst);
    if ( ! $inf )
    {
        $setup->msg($FATAL, 'error_create_inf_from_config',
                            "$orig_confdir/$originst");
        $setup->doExit(1);
    }
    $setup->{inf} = $inf;
    $setup->{inf}->{slapd}->{ServerIdentifier} = $orig_confdsid;
    $setup->{inf}->{slapd}->{config_dir} = $orig_confdir;
    $setup->{inf}->{slapd}->{Instances} = \%instances;

    my $dialogmgr = new DialogManager($setup, $res, $TYPICAL);

    require RegDSDialogs;
    require SetupDialogs;
    require ConfigDSDialogs;
    require ASDialogs;

    my @dialogs = RegDSDialogs->getDialogs();

    $dialogmgr->addDialog(@dialogs);

    my $rc = $dialogmgr->run();
    if ( $rc )
    {
        $setup->doExit(1);
    }
    $new_confdsid = $setup->{inf}->{slapd}->{ServerIdentifier};
    $new_confdir = $setup->{inf}->{slapd}->{config_dir};
    if ( $orig_confdsid ne $new_confdsid )
    {
        my $newinst = "slapd-$new_confdsid";
        $inf = createInfFromConfig("$instconfigdir/$newinst", $newinst);
        if ( ! $inf )
        {
            $setup->msg($FATAL, 'error_create_inf_from_config',
                                "$instconfigdir/$newinst");
            $setup->doExit(1);
        }
        $setup->{inf}->{slapd} = $inf->{slapd};
        $setup->{inf}->{slapd}->{Instances} = \%instances;
    }
    $setup->{inf}->{General}->{ConfigDirectoryLdapURL} = "ldap://" . $fqdn . ":". $setup->{inf}->{slapd}->{ServerPort} . "/o=NetscapeRoot";

    $dialogmgr->resetDialog();
    @dialogs = SetupDialogs->getRegDialogs();
    push @dialogs, ConfigDSDialogs->getRegDialogs();
    push @dialogs, ASDialogs->getDialogs();

    $dialogmgr->addDialog(@dialogs);

    $rc = $dialogmgr->run();
    if ( $rc )
    {
        $setup->doExit(1);
    }
    $adminuid = $setup->{inf}->{General}->{ConfigDirectoryAdminID};
}



# Get the ConfigDS's rootDN password
print("\n==============================================================================\n");
$setup->msg('register_new_confds', $new_confdsid);
$setup->{inf}->{slapd}->{RootDNPwd} =
    reg_get_passwd($setup, 'input_rootdn_passwd', $new_confdsid);

if ( ($#admConfKeys >= 0 && ($orig_confdsid ne $new_confdsid)) ||
      $#admConfKeys < 0 )
{
    @errs = ();
    # First, let's register the Configuration Directory itself
    while (!createConfigDS($setup->{inf}, \@errs))
    {
        foreach my $err (@errs)
        {
            if ( $err eq "suffix_already_exists" )
            {
                goto out;
            }
        }
        $setup->{inf}->{slapd}->{RootDNPwd} =
                reg_get_passwd($setup, 'input_rootdn_passwd', $new_confdsid);
        @errs = ();
    }
out:
}

# If we don't have it, prompt for the Admin password
if (!$setup->{inf}->{General}->{ConfigDirectoryAdminPwd} ||
    "" eq $setup->{inf}->{General}->{ConfigDirectoryAdminPwd})
{
    $ans = reg_get_passwd($setup, 'input_admin_passwd', $adminuid);
    $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} = $ans;
}

@errs = ();
while (!registerDSWithConfigDS($new_confdsid, \@errs, $setup->{inf}))
{
    $setup->msg($WARN, 'error_register_configds', $new_confdsid);
    $ans = reg_get_passwd($setup, 'input_admin_passwd', $adminuid);
    $setup->{inf}->{General}->{ConfigDirectoryAdminPwd} = $ans;
    @errs = ();
}

my $hassubinst = 0;
# Then, register the rest of the Directory Servers, if any
my %subinstances = ();    # hash without the Config DS
%instances = %{$setup->{inf}->{slapd}->{Instances}};
foreach my $subconfdir (keys %instances)
{
    my @subinsts = @{$instances{$subconfdir}};
    foreach my $subinst ( @subinsts )
    {
        if ( ("$subinst" ne "slapd-" . $new_confdsid) ||
             ($subconfdir ne $new_confdir) )
        {
            if ( 0 == $hassubinst )
            {
                $hassubinst = 1;
                print("\n==============================================================================\n");
                $setup->msg('register_subds');
            }
            my $subid = $subinst;
            $subid =~ s/slapd-//;
            my $passwd =
                reg_get_passwd($setup, 'input_rootdn_passwd_sub', $subid, $subid);
            # if the password is not given, we don't register the server
            next if ( "" eq $passwd || !$passwd );
            my $subinf = createInfFromConfig("$subconfdir/$subinst", $subinst);
            if ( ! $subinf )
            {
                $setup->msg($FATAL, 'error_create_inf_from_config',
                                    "$subconfdir/$subinst");
            }
            else
            {
                # If we're switching the config DS, we want to force updating the
                # PTA plug-in since it's configured for the old config DS.
                my $force_pta = 0;
                if ( $orig_confdsid ne $new_confdsid ) {
                    $force_pta = 1;
                }

                $setup->{inf}->{slapd} = $subinf->{slapd};
                $setup->{inf}->{slapd}->{RootDNPwd} = $passwd;
                push @{$subinstances{$subconfdir}}, $subinst;
                $done = 0;
                while ( !$done && !createSubDS($setup->{inf}, \@errs, $force_pta) )
                {
                    $setup->msg($FATAL, @errs);
                    $passwd = reg_get_passwd($setup, 'input_rootdn_passwd_sub',
                                                        $subid, $subid);
                    if ( "" eq $passwd || !$passwd )
                    {
                        $done = 1;
                        pop @{$subinstances{$subconfdir}};
                    }
                    else
                    {
                        $setup->{inf}->{slapd}->{RootDNPwd} = $passwd;
                    }
                }

                # add the aci that allows the admin user to administer the server
                @errs = ();
                if (!addConfigACIsToSubDS($setup->{inf}, \@errs)) {
                    $setup->msg(@errs);
                    $setup->doExit(1);
                }
            }
        }
    }
}

my @subkeys = keys %subinstances;
if ( $#subkeys >= 0 )
{
    @errs = ();
    if ( !registerScatteredDSWithConfigDS($setup->{inf}, \@errs, \%subinstances) )
    {
        $setup->msg($FATAL, @errs);
        $setup->doExit(1);
    }
}

# Configure and register the admin server instance.
# Generate a new inf for the config DS and override
# the old slapd data from the last instance we registered.
$new_confdir = $setup->{inf}->{slapd}->{config_dir};
my $newinst = "slapd-$new_confdsid";
my $inf = createInfFromConfig("$instconfigdir/$newinst", $newinst);
if ( ! $inf )
{
    $setup->msg($FATAL, 'error_create_inf_from_config', "$instconfigdir/$newinst");
    $setup->doExit(1);
}
$setup->{inf}->{slapd} = $inf->{slapd};

# need these manually set these 2 parameters
$setup->{inf}->{slapd}->{UseExistingMC} = "yes";
$setup->{inf}->{slapd}->{SlapdConfigForMC} = "yes";
if ( !$setup->{reconfigas} )
{
    if ( !createAdminServer($setup) )
    {
        $setup->msg($FATAL, 'error_create_adminserver');
        $setup->doExit(1);
    }
}
else
{
    if ( !reconfigAdminServer($setup) )
    {
        $setup->msg($FATAL, 'error_reconfig_adminserver');
        $setup->doExit(1);
    }
}
$setup->msg('end_ds_registration');

$setup->doExit(0);

