#{{PERL-EXEC}}

# Create migration-directory to prepare future migration

# enable the use of Perldap functions
require DynaLoader;

use Getopt::Std;
use Mozilla::LDAP::Conn;
use Mozilla::LDAP::Entry;
use Mozilla::LDAP::LDIF;
use Mozilla::LDAP::Utils qw(:all);
use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API
use Time::localtime;
use File::Basename;
use Class::Struct ; 

#######################################################################################################

sub usage {
	print(STDERR "\nUsage: $0 -d migrationdirectory [-t tracelevel] [-L logfile]\n");
	print(STDERR "************** parameters in brackets are optionals, others are required **************\n");
	print(STDERR " Opts: -d migrationdirectory    - Migration directory\n");
	print(STDERR "     : [-t tracelevel]          - Specify the level of trace (0..3)\n");
	print(STDERR "     : [-L logfile]             - Specify the file to log the migration report \n");
}

########################################################################################################

BEGIN {
  
	require 'uname.lib' ;
	$isNT = -d '\\';
	$PATHSEP = $isNT ? "\\" : "/";
	${SEP} = $isNT ? ";" : ":" ; 
	@INC = ( '.', '../../../admin/admin/bin');
	grep { s@/@\\@g } @INC if $isNT;
	$script_suffix = $isNT ? ".bat" : "";
	$exe_suffix = $isNT ? ".exe" : "";
	# NT needs quotes around some things unix doesn't
	$quote = $isNT ? "\"" : "";

	# If this variable is set, all file/directory creation will make sure the mode
	# and ownership of the destination is the same as the source
	$PRESERVE = 1 if (!$isNT);
	$script_suffix = $isNT ? ".bat" : "";
	$exe_suffix = $isNT ? ".exe" : "";
	if ($isNT) 
	{
		$os = "WINNT";
	} 
	else 
	{
        $os = &uname("-s");
        if ($os eq "SunOS") {
            $isSolaris9 = ( &uname("-r") eq "5.9" );
        }
	}

	if ($isNT) 
	{
		# we have to pass batch files directly to the NT command interpreter
		$com_spec = $ENV{ComSpec};
		if (!$com_spec) 
		{
			$com_spec = $ENV{COMSPEC};
		}
		if (!$com_spec || ! -f $com_spec) 
		{
			# find the first available command interpreter
			foreach $drive (c..z) 
			{
				$com_spec = "$drive:\\winnt\\system32\\cmd.exe";
				last if (-f $com_spec);
				$com_spec = undef;
			}
			if (! $com_spec) 
			{
				# punt and pray
				$com_spec = 'c:\winnt\system32\cmd.exe';
			}
		}
	}
	if ( $os eq "AIX" ) 
	{
		$dll_suffix = "_shr.a";
	}	
	elsif ( $os eq "HP-UX" ) 
	{
		$dll_suffix = ".sl";
	}	
	elsif ( $os eq "WINNT" ) 
	{
		$dll_suffix = ".dll";
	}	
	else 
	{
		$dll_suffix = ".so";
	}	

	$slapdExecName = $isNT ? 'slapd.exe' : 'ns-slapd';
	select STDERR;
	$| = 1;
	select STDOUT;
	$| = 1;
}

SWITCH: {
	if ($os eq "AIX") 
	{
		$LIB_PATH = "LIBPATH" ;
		last SWITCH ;
	}
	if ($os eq "HP-UX") 
	{
		$LIB_PATH = "SHLIB_PATH" ;
		last SWITCH ;
	}
	if ($isNT) 
	{
		$LIB_PATH = "PATH" ;
		last SWITCH ;
	}
	else 
	{
		$LIB_PATH = "LD_LIBRARY_PATH" ;
		last SWITCH ;
	}
}  

%stdIncludes = (
	"."                        => "\n",
	".."                       => "\n",
	"30ns-common.ldif"         => "\n",
	"50ns-mail.ldif"           => "\n",
	"50ns-news.ldif"           => "\n",
	"50iplanet-servicemgt.ldif"=> "\n",
	"50ns-mcd-browser.ldif"    => "\n",
	"50ns-proxy.ldif"          => "\n",
	"00core.ldif"              => "\n",
	"50ns-admin.ldif"          => "\n",
	"50ns-mcd-config.ldif"     => "\n",
	"50ns-value.ldif"          => "\n",
	"05rfc2247.ldif"           => "\n",
	"50ns-calendar.ldif"       => "\n",
	"50ns-mcd-li.ldif"         => "\n",
	"50ns-wcal.ldif"           => "\n",
	"05rfc2927.ldif"           => "\n",
	"50ns-certificate.ldif"    => "\n", 
	"50ns-mcd-mail.ldif"       => "\n", 
	"50ns-web.ldif"            => "\n",
	"11rfc2307.ldif"           => "\n",
	"50ns-compass.ldif"        => "\n",
	"50ns-media.ldif"          => "\n", 
	"20subscriber.ldif"        => "\n",
	"50ns-delegated-admin.ldif"=> "\n", 
	"50ns-mlm.ldif"            => "\n",
	"25java-object.ldif"       => "\n",
	"50ns-directory.ldif"      => "\n", 
	"50ns-msg.ldif"            => "\n",
	"28pilot.ldif"             => "\n",
	"50ns-legacy.ldif"         => "\n", 
	"50ns-netshare.ldif"       => "\n"
	);


${LogFileReport}	= "" ;
${curdir}			= getCwd();
${root}				= "{{DS-ROOT}}" ;

open(LOGFILE, ">> $LogFileReport");

&getParameters() ;

${oldDir}			= &normalizeDir("${root}");
${oldSlapdExecDir}	= "${oldDir}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}";
${oldRootServerLib}	= "${oldDir}${PATHSEP}lib";

$ENV{"$LIB_PATH"}	= "${oldRootServerLib}${SEP}".$ENV{"$LIB_PATH"} ;
$ENV{"PATH"}		= ".${SEP}".$ENV{"PATH"};

${migrate_dir}		= &normalizeDir("${migrate_dir}");

if ($isSolaris9) 
{
	$ENV{"$LIB_PATH"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.005_03${PATHSEP}lib${PATHSEP}sun4-solaris${PATHSEP}CORE${SEP}".$ENV{"$LIB_PATH"} ;
}

if ($isNT) 
{
	$ENV{"$LIB_PATH"}	= $ENV{"$LIB_PATH"}."${SEP}$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}" ;
}

# Get the version 
($Version, $Minor, $Micro)	= &getVersion(${oldSlapdExecDir}, ${oldRootServerLib});

# Check the server runs in 64 bit mode. If so, change the execution directory and the server library path 
if ( &is_64bitEnabledServer(${oldDir}) ){ 
	  printMsg("\nThe server under ${oldDir} runs in a 64 bit mode"); 
	  &setup_64bitServer(${oldDir}); 
} 
else { 
	  printMsg("\nThe server under ${oldDir} runs in a 32 bit mode"); 
} 

opendir( SRC, "$oldDir${PATHSEP}") or die "Can't open directory $oldDir: $!: ";

printMsg("\n\n  ******   Migration - Phase 1  ******\n\n");
printMsg("\n\n  This phase may be long and space consuming (depending of the databases size)\n\n");
printMsg("\n\n  Do you want to continue Yes/No [No] ?");
$continue = <STDIN>;
if ( !($continue =~ /y|yes/i))
{
	printMsg("\n\n  ... Exiting.\n");
	exit(1);
}

local ( @files ) = readdir ( SRC );
closedir( SRC );
foreach $file ( @files )
{
	$oldHome = "$oldDir${PATHSEP}$file" ;
	if ($oldHome =~ /[\"]?(.*)?[\"]?/)
	{ 
		$oldHome = $1 ;
	}
	if ($oldHome =~ m@^(.*)/slapd-([^/]*)[/]?$@)
	{
		my $oldDir  = $1 ;
		my $type    = "slapd" ;
		my $oldname = $2 ;
		if ($isNT)
		{
			$oldDir  = lc($oldDir)     ;
			$type    = "slapd"       ;
			$oldname = lc($oldname)    ;
			$oldHome = lc($oldHome)    ;
			grep { s@/@\\@g } $oldDir  ;
			grep { s@/@\\@g } $oldHome ;
		}

		printMsg("\n** CAUTION: data will be lost after uninstallation**\n");
		printMsg("\nDo you want to preserve ${oldHome} Yes/No [Yes] ?");
		$answer = <STDIN> ;
		if ( !($answer =~ /y|yes/i)) 
		{
			printMsg("\n... ${oldHome} skipped.\n");
		}
		else
		{
			MigrateInstance($oldDir, $type, $oldname, $oldHome);
		}
		
    }
}
# Copy adm.conf (info usefull for restoration)
&CopyAdm();

&getInstallPath();


close(LOGFILE);

printMsg("\n\n  ******   End of migration - Phase 1  ******\n\n");

sub MigrateInstance()
{
	$oldDir  = shift;
	$type    = shift;
	$oldname = shift;
	$oldHome = shift;

printTrace(" oldHome: $oldHome\n", 3);
printTrace(" oldDir: $oldDir\n", 3);
printTrace(" type: $type\n", 3);
printTrace(" oldname: $oldname\n", 3);

	# old parameters
	${oldConfDir}     = "" ;
	${oldlocaluser}   = "" ;
	${oldgid}         = "" ;
	${olduid}         = "" ;
	${oldSchemaDir}	= "";
	${oldDSEldif}	= "";
	${migrateHome}	= "";
	${migrateConfDir}	= "";
	${migrateSchemaDir}	= "";
	${migrateDSEldif}	= "";

	# specify the level of trace
	$TRACELEVEL=1;

	# get input users
	${oldHome}         = "${oldDir}${PATHSEP}$type-$oldname" ;    
	${oldConfDir}      = "${oldHome}${PATHSEP}config${PATHSEP}" ;
	${oldSchemaDir}    = "${oldConfDir}schema${PATHSEP}";
	${oldDSEldif}      = "${oldConfDir}dse.ldif";        

	${migrateHome}     = "${migrate_dir}${PATHSEP}$type-$oldname" ;    
	${migrateConfDir}  = "${migrateHome}${PATHSEP}config${PATHSEP}" ;
	${migrateSchemaDir}= "${migrateConfDir}schema${PATHSEP}";
	${migrateDSEldif}  = "${migrateConfDir}dse.ldif";        

printTrace("migrateHome: $migrateHome++migrateConfDir: $migrateConfDir++migrateSchemaDir: $migrateSchemaDir++migrateDSEldif: $migrateDSEldif++oldHome: $oldHome++oldConfDir: $oldConfDir++oldSchemaDir: $oldSchemaDir++oldDSEldif: $oldDSEldif\n", 3);

	# Backends migrated (Backend CN attribute value)
	@BACKENDS    = () ; 
	#store the backend instances to migrate
	@LDBM_backend_instances = ();

	# get the hostname of the DAP server
	$LDAPservername = &getLDAPservername(${oldDSEldif});
	($oldlocaluser, $olduid, $oldgid) = getolduid_gid(${oldDSEldif});

	# Shutdown the legacy Directory instance
	&stopServer($oldDir, 'slapd-'.$oldname);

	# Copy the data and the conf
	$oldkeyfile="";
	$oldcertfile="";
	($oldkeyfile, $oldcertfile) = CopyDSEldif($oldDSEldif, $migrateHome, $migrateConfDir, $migrateDSEldif);
	&CopySchema($oldSchemaDir, $migrateSchemaDir, $oldSchemaDir);
	&CopySSL($oldkeyfile, $oldcertfile);
	&CopyCertmap();

	# Migrate LDBM backend instances
	&migrateLDBM_backend_instances();

	if (@BACKENDS) {
	  $migrateLDIF = "${migrateConfDir}${PATHSEP}ldif${PATHSEP}";
	  if  (!(-d $migrateLDIF)) {
		  mkdir($migrateLDIF,0777) or die "can't create directory $migrateLDIF. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $migrateLDIF );
	  }
	  $oldLDIF = "${oldConfDir}${PATHSEP}ldif${PATHSEP}";
	  &manydb2Ldif($oldLDIF);
	}

}

###########################################################################################
# get input users
sub getParameters {
	my $exit = 0 ;
	my $i    = 0;

	while ($i <= $#ARGV) 
	{
		if ("$ARGV[$i]" eq "-d") 
		{ 
			# migration directory
			my $value = $ARGV[++$i] ;	
			$migrate_dir = $value."${PATHSEP}";
			if ( ! -d "$migrate_dir") 
			{
				mkdir($value, 0777) or die "\nCan't create directory $value. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $migrate_dir );
			}
		} elsif ("$ARGV[$i]" eq "-t") {
			# TRACELEVEL
			my $value = $ARGV[++$i] ;	
			if ($value =~ /[0-3]/) 
			{
				$TRACELEVEL = $value ;
			}
			else {
				print("\nThe tracelevel must belong to 0..3 interval");
				&usage();
				exit();
			}
		} elsif ("$ARGV[$i]" eq "-L") { # migration logfile
			$LogFileReport = $ARGV[++$i] ;
		} 
		else 
		{
			print("\nThe option $ARGV[$i] is not recognized");
			&usage() ;
			exit(1);
		}
		$i++;
	}

	if (! $migrate_dir) 
	{
		print("\nThe migration directory is missing");
		$exit = 1;
	}
	if ((! $LogFileReport) && $root) 
	{
		($sec, $min, $hour, $dd, $mm, $yy) = &GetTime();
		$LogFileReport  = "${root}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log";
	}
	if ($exit) 
	{
		&usage() ;
		exit(1);
	}
}

###################################################################################################
sub CopyDSEldif {
	my $oldDSEldif = shift;
	my $migrateHome = shift;
	my $migrateConfDir = shift;
	my $migrateDSEldif = shift;
	my $oldkeyfile="";
	my $oldcertfile = "";

	printMsg("\n\n------------------------------------------------------------------------\n\n");
	printMsg("Copying the configuration file ...\n");

	open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: ";
	my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ;
	while ($entry = readOneEntry $in) 
	{
		$typeOfEntry = getTypeOfEntry($entry);
		SWITCH: 
		{
			if ($typeOfEntry eq "LDBM_BACKEND_INSTANCE")
			{
				parseLDBM_backend_instance($entry);
				last SWITCH;
			}
			if ($typeOfEntry eq "SECURITY")
			{
				my $temp_key;
				my $temp_cert;
				($temp_key, $temp_cert)=parseSecurity($entry);
				if ( !$oldkeyfile )
				{
					$oldkeyfile = $temp_key;
				}
				if ( !$oldcertfile )
				{
					$oldcertfile = $temp_cert;
				}
				last SWITCH;
			}
		}
	}
	close(DSELDIF);

	if ( ! (-d ${migrateHome} ) ) 
	{
		mkdir($migrateHome, 0777)  or die "\nCan't create directory $migrateHome. \nPlease check you have enough rights to create it.\n" if !( -e $migrateHome );
	}
	if ( ! (-d ${migrateConfDir} ) ) 
	{
		mkdir($migrateConfDir, 0777) or die "\nCan't create directory $migrateConfDir. \nPlease check you have enough rights to create it.\n" if !( -e $migrateConfDir );
	}

	&copyBinFile($oldDSEldif, $migrateDSEldif);

	printMsg("\n$oldDSEldif copied.\n");
	
	return($oldkeyfile, $oldcertfile);
}

#############################################################################
# returns the "type of an entry". If the entry is not to be migrated its type is "NOT_MIGRATED_TYPE"

sub getTypeOfEntry{
	my $entry = shift;
	my $DN = $entry->getDN(1) ; # 1 is to normalize the returned DN 
	if (($DN =~ /cn=ldbm database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsBackendInstance"))) 
	{
		return "LDBM_BACKEND_INSTANCE";
	}
	if ($DN =~ /^cn=config$/i) 
	{
		return "CONFIG_NODE";
	}
	if ($DN =~ /cn=encryption,cn=config$/i) 
	{
		return "SECURITY";
	}
	return "NOT_MIGRATED_TYPE";
}

#############################################################################
# returns 1 if the objectclass given in parameter is present in the objectclasses values of the entry 
# given in parameter, 0 else

sub isObjectclass {
    my $entry       = shift;
    my $objectclass = shift;
    return ($entry->hasValue("objectclass",$objectclass,1));
}

#############################################################################
sub migrateLDBM_backend_instances {
	my $printLine = 0;

	foreach $entry (@LDBM_backend_instances) 
	{
		if ( $printLine == 0 )
		{
			$printLine = 1;
			printMsg("\n\n------------------------------------------------------------------------\n\n");
			printMsg("Backup user data ...");
		}
		my $DN    = $entry->getDN(1); # 1 is to normalize the DN
		my $CN    = $entry->{cn}[0];
		if ($DN =~/cn=netscaperoot,cn=ldbm database/i)
		{
#			printTrace("\n\n*** INFORMATION - NetscapeRoot is NOT preserved ***\n",0);
			push @BACKENDS, $CN;
		}
		else 
		{
			push @BACKENDS, $CN;
		}
	}
}

sub parseLDBM_backend_instance {
	my $entry = shift;
	push @LDBM_backend_instances, $entry;
}


#############################################################################
# Get real cert and key pathnames
sub parseSecurity {
	my $security   = shift;
	my $oldkeyfile;
	my $oldcertfile;

    # if nsKeyfile and nsCertfile exist in old, must rename with
    # the new instance path
    my @oldkeyval = $security->getValues("nsKeyfile");
	if ( !$oldkeyfile && @oldkeyval)
	{
		$oldkeyfile = $oldkeyval[0];
	}
    my @oldcertval = $security->getValues("nsCertfile");
    if ( !$oldcertfile && @oldcertval )
    {
        $oldcertfile = $oldcertval[0];
    }
	return($oldkeyfile, $oldcertfile);

}

#############################################################################
# printMsg print message to the user standard output.  

sub printMsg {

  my $TypeMsg = shift ;
  my $Msg     = shift ;
  my $LineNb  = shift ;
  if ($LineNb) {
    printTrace("Line: $LineNb, $TypeMsg, $Msg");
  }
  else {
    printTrace("$TypeMsg $Msg");
  }
}

#############################################################################
#  print message error to the user standard output.  

sub printTrace {

  my $Msg     = shift ;
  my $level   = shift ;
  my $sep     = shift ;

  if ($sep) {
      print "\n-------------------------------------------------------------------------";
      print LOGFILE "\n-------------------------------------------------------------------------";
  }
  
  if ($level <= $TRACELEVEL) {
    print($Msg);    
    print LOGFILE $Msg ;
  }
}  

#############################################################################

# this is used to run the system() call, capture exit and signal codes,
# and die() upon badness; the first argument is a directory to change
# dir to, if any, and the rest are passed to system()
sub mySystem {
	my $rc = &mySystemNoDie(@_);
	my ($dir, @args) = @_;
	if ($rc == 0) 
	{
		# success
	} 
	elsif ($rc == 0xff00) 
	{
		die "Error executing @args: error code $rc: $!";
	} 
	elsif ($rc > 0x80) 
	{
		$rc >>= 8;
		die "Error executing @args: error code $rc: $! ** $dir, @args, @_";
	} 
	else 
	{
		if ($rc &   0x80) 
		{
			$rc &= ~0x80;
		} 
		die "Error executing @args: received signal $rc: $!";
	}
	# usually won't get return value
	return $rc;
}

# This version does not die but just returns the error code
sub mySystemNoDie {
	my ($dir, @args) = @_;
	if ($dir && ($dir ne "")) 
	{
		chdir($dir) or die "Could not change directory to $dir: $!";
	}
	my $cmd = $args[0];

	# the system {$cmd} avoids some NT shell quoting problems if the $cmd
	# needs to be quoted e.g. contains spaces; the map puts double quotes
	# around the arguments on NT which are stripped by the command
	# interpreter cmd.exe; but don't quote things which are already quoted
	my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @args;
	my $rc = 0;
	if ($cmd =~ /[.](bat|cmd)$/) 
	{
		# we have to pass batch files directly to the NT command interpreter
		$cmd = $com_spec;
		$rc = 0xffff & system {$cmd} '/c', "\"@fixargs\"";
	} 
	else 
	{
		$rc = 0xffff & system {$cmd} @fixargs;
	}
	chdir(${curdir})  or die "Could not change directory to $curdir: $!";
	return $rc;
}

###########################################################################################
#                                                                                         #
#                    Export/Import of the backends in @BACKENDS                           #
#                                                                                         #
###########################################################################################

sub manydb2Ldif {
	my $ldif_dir = shift;  

	$ENV{"$LIB_PATH"}="${oldRootServerLib}${SEP}".$ENV{"$LIB_PATH"};
	$ENV{"NETSITE_ROOT"} = "${root}";

	if (! $ldif_dir) 
	{ 
		$ldif_dir = $migrate_dir ;
	}
	if  (!(-d $ldif_dir)) 
	{
		mkdir($ldif_dir,0777) or die "can't create directory $ldif_dir to store temporary files. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $ldif_dir );
	}
	chdir($oldSlapdExecDir)  or die "Could not change directory to $oldSlapdExecDir: $!";

	foreach $backend (@BACKENDS) 
	{
		printMsg("\n");
		my $ldif = "${ldif_dir}$backend.ldif" ;

		&db2Ldif($ldif, $backend);

		# Trick: we can not generate directly in the migration directory because of permissions.
		# So just generate in the "std" place and then copy....
		copyBinFile($ldif, "$migrateLDIF$backend.ldif");

		printMsg("\nDone for backend $backend.\n");
		printMsg("\n---\n");
		unlink($ldif) ;
	}
	chdir($curdir)  or die "Could not change directory to $curdir: $!";   
}

sub db2Ldif {
	my $ldif            = shift ;
	my $backend         = shift ;
	my $include_suffix  = shift ;
	my $db2ldif_param ;

	if ($include_suffix) 
	{
		$db2ldif_param   = "db2ldif -D $oldHome -n $backend -a $ldif -s \"$include_suffix\"";
	}
	else 
	{
		$db2ldif_param   = "db2ldif -D $oldHome -n $backend -a $ldif";
	}
	open(DB2LDIF, "${quote}${quote}$slapdExecName${quote} $db2ldif_param${quote} 2>&1 |") or die "Could not run ns-slapd program db2ldif\n";

	sleep(1); # allow some data to accumulate in the pipe

	my $ii = 0;
	while (<DB2LDIF>) 
	{
		++$ii;
		if (($ii % 250) == 0) 
		{
			printMsg("  Processing...\n");
		}
		printMsg($_);
	}
	close(DB2LDIF);
}

#############################################################################

sub getLDAPservername {
	my ${oldDSEldif} = shift;
	my $oldLDAPservername = "";
	my $localhost = "nsslapd-localhost";
	my $done = 0;

	open(OLDDSELDIF, "< $oldDSEldif") or die "\nError: could not open config file $oldDSEldif \n";
	my $in = new Mozilla::LDAP::LDIF(*OLDDSELDIF) ;
	while ( ($entry = readOneEntry $in)  && ($done == 0) )
	{
		my $DN = $entry->getDN(1) ;
		if ($DN =~ /^cn=config$/i) 
		{
			my @values = $entry->getValues($localhost);
			if ($entry->size($localhost)) 
			{
				$oldLDAPservername = $values[0];
				printTrace("\nName of the old LDAP server: $oldLDAPservername",3);
			}
			$done = 1;
		}
	}    
	close(OLDDSELDIF);
	return $oldLDAPservername ;
}

########################################################################
#                                                                      #
#                    Stopping the Server                               #
#                                                                      #
########################################################################

sub stopServer {
	my $root = shift;
	my $name = shift;
	$maxStopIterations = 5;

	printTrace("\nShutting down server $oldHome . . .\n", 0);

	$ENV{"$LIB_PATH"}="${oldRootServerLib}${SEP}".$ENV{"$LIB_PATH"};
	$stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop' . $script_suffix . $quote;

	if (! -f $stopCmd) 
	{
		$stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop-slapd' . $script_suffix . $quote;
	}

	if (! -f $stopCmd) 
	{
		# no stop command, probably a 1.X system; for NT, we'll try net stop
		# for unix, we'll get the pid and kill it
		if ($isNT) 
		{
			$stopCmd = 'net stop ' . $name;
		} 
		else 
		{
			# see if there is a pid file
			$pidfile = $root . $PATHSEP . $name . $PATHSEP . 'logs' .
			$PATHSEP . 'pid';
			if (open(PIDFILE, $pidfile)) 
			{
				chomp($pid = <PIDFILE>);
				close(PIDFILE);
				while ($maxStopIterations-- && !$exitCode) 
				{
					$exitCode = kill(15, $pid);
				}
				$stopCmd = undef;
			}
		}
	}

	# keep looping until the stop cmd returns an error code, which usually
	# means that what ever we want to stop is stopped, or some other error
	# occurred e.g. permission, or no such service
	$exitCode = &runAndIgnoreOutput($stopCmd);
	while ($stopCmd && $maxStopIterations-- && $exitCode) 
	{
		$exitCode = &runAndIgnoreOutput($stopCmd);
	}

	if (!$maxStopIterations) 
	{
		print "Warning: could not shutdown the server: $!\n";
	}
	sleep(10) ;
	$exitCode = 0;
}

sub runAndIgnoreOutput {
	my $cmd = shift;
	printMsg(".");
	open(RUNCMD, "${quote}$cmd${quote} 2>&1 |") or die "Error: could not run $cmd: $!";
	printMsg(".");
	sleep(1); # allow pipe to fill with data
	printMsg(".");
	while (<RUNCMD>) { }
	my $code = close(RUNCMD);
	return $?;
}

###########################################################################################
#                                                                                         #
#                    Copy directory and files functions                                   #
#                                                                                         #
###########################################################################################

sub copyDir {
	my	$src = shift;
	my	$dest = shift;
	my  $exclude = shift;

	opendir( SRC, $src ) or die "Can't open directory $src: $!: ";
	my $mode;
	my $uid;
	my $gid;
	mkdir ( $dest , 0755 ) or die "\nCan't create directory $dest. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $dest );
	if ($PRESERVE) 
	{
		$mode = (stat($src))[2];
		($uid, $gid) = (stat(_))[4..5];
		# Make sure files owned by the old user are owned by the
		# new user
		chown $uid, $gid, $dest;
		chmod $mode, $dest;
	}
	local ( @files ) = readdir ( SRC );
	closedir( SRC );
	for ( @files ) 
	{
		if ( $_ eq "." || $_ eq ".." ) 
		{
			next;
		} 
		elsif ( $exclude && /$exclude/ ) 
		{
			next;
		} 
		elsif( -d "$src${PATHSEP}$_")  
		{
			&copyDir ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_" ); 
		} else {
			&copyBinFile ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_");
		}
	}
}

sub copyBinFile {
	my	$src = shift;
	my	$dest = shift;
	my  $buf = "";
	my  $bufsize = 8192;

	open( SRC, $src ) || die "Can't open $src: $!\n";
	# if we are given a directory destination instead of a file, extract the
	# filename portion of the source to use as the destination filename
	if (-d $dest) 
	{
		$dest = $dest . $PATHSEP . &basename($src);
	}
	open( DEST, ">$dest" ) || die "Can't create $dest: $!\n";
	binmode SRC;
	binmode DEST;
	if ($PRESERVE) 
	{
		$mode = (stat($src))[2];
		($uid, $gid) = (stat(_))[4..5];
		chown $uid, $gid, $dest;
		chmod $mode, $dest;
	}
	while (read(SRC, $buf, $bufsize)) 
	{
		print DEST $buf;
	}
	close( SRC );
    close( DEST );
}

#############################################################################
sub getolduid_gid {
	my $oldDSEldif = shift;
	my $oldlocaluser ;
	my $localuserAttr = "nsslapd-localuser";
	my $entry ;
	my $done = 0;
	if (! $isNT) 
	{
		open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: ";
		my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ;
		while ( ($entry = readOneEntry $in)  && ($done ==0) )
		{
			$typeOfEntry = getTypeOfEntry($entry);
			if ($typeOfEntry eq "CONFIG_NODE") 
			{
				$oldlocaluser = $entry->{$localuserAttr}[0] if ($entry->exists($localuserAttr));
				$done = 1;
			}
		}
		close(DSELDIF);
		($olduid, $oldgid) = (getpwnam("$oldlocaluser"))[2..3] ;
		return ($oldlocaluser, $olduid, $oldgid) ;
	}
	else 
	{
		return ();
	}
}

sub getVersion { 
	my $execDir     = shift; 
	my $serverLib   = shift; 
	my $version     = 0; 
	my $minor       = 0; 
	my $micro       = ""; 
	my $buildNumber = 0; 

	# find the slapd executable 
	$prog = $execDir. $slapdExecName; 

	if (! -f $prog) 
	{
		die "Could not run slapd program $prog: $!"; 
	} 
	else 
	{
		chdir($execDir); 
	} 

	$ENV{"$LIB_PATH"}="$serverLib${SEP}".$ENV{"$LIB_PATH"}; 

	# read the version from the slapd program
	open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or
		die "Could not run slapd program $prog: $!";

	${dest} = "${migrate_dir}${PATHSEP}version";
	open(DEST, ">$dest") || die "\nCan't create $dest ($!). \nPlease check you have enough rights to create it \n" if !( -e $dest);

	sleep(1); # allow some data to accumulate in the pipe
	while (<F>) 
	{
		#print;
		$full = $_;
		print DEST $full;
		if (/^Netscape-Directory\/(\d+)\.(\d+)\s+(\S+)/) 
		{
			$version     = $1;
			$minor       = $2;
			$buildNumber = $3;
			last;
		}
		elsif (/^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) 
		{ 
			# we can have restricted-mode or restriced-mode ...
			$version     = $1;
			$minor       = $2;
			$buildNumber = $3;
			last;
		}
		elsif (/^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) 
		{
			$version     = $1;
			$minor       = $2;
			$buildNumber = $3;
			last;
		}
		elsif (/^Sun-ONE-Directory\/(\d+)\.(\d+)\s+(\S+)/i) 
		{
			$version     = $1;
			$minor       = $2;
			$buildNumber = $3;
			last;
		}
		elsif (/^Sun-ONE-Directory\/(\d+)\.(\d+)_(\S+)\s+(\S+)/i) 
		{
			$version     = $1;
			$minor       = $2;
			$micro = "_$3";
			$buildNumber = $4;
			last;
		}
	}
	close(DEST);
	$code = close(F);
	$ENV{"$LIB_PATH"}="${oldRootServerLib}${SEP}".$ENV{"$LIB_PATH"};
	
	if ($version == 0) 
	{
		die "\nCould not determine version of the directory server in $execDir: \n";
	}

	# distinguish the 4.1 and the 4.11 thanks to the buildNumber
	if (($version == 4) && ($minor == 1))
	{
		if (! ($buildNumber =~ /^B99\.16/)) 
		{
			# it's not a 4.1 Netscape Directory Server => it's a 4.11
			$minor = 11 ;
		}
	}
	chdir($curdir) or die "Could not change directory to $curdir: $!" ;

	return ( $version, $minor , $micro);
}

###############################################################################################
sub normalizeDir {
	my $dir = shift ;
	my $dir_prec = "" ;
	while ($dir_prec ne $dir) 
	{
		$dir_prec = $dir ;
		if ($isNT) 
		{
			grep { s@\\\\@\\@g } $dir ;
		}
		else 
		{
			grep { s@//@/@g } $dir ;
		}
	}
	return $dir ;
}

###############################################################################################

sub GetTime {
    my $tm = localtime;
    (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900);
    $sec  = "0$sec"  unless $sec > 9  ;
    $min  = "0$min"  unless $min > 9  ;
    $hour = "0$hour" unless $hour > 9 ;
    $dd   = "0$dd"   unless $dd > 9   ;
    $mm   = "0$mm"   unless $mm > 9   ;
    return ($sec, $min, $hour, $dd, $mm, $yy);
}

###############################################################################################
# get current directory

sub getCwd {
	my $command = $isNT ? "cd" : "/bin/pwd";

	open(PWDCMD, "$command 2>&1 |") or
	die "Error: could not execute $command: $!";
	# without the following sleep, reading from the pipe will
	# return nothing; I guess it gives the pwd command time
	# to get some data to read . . .
	sleep(1);
	my $currentdir;
	while (<PWDCMD>) 
	{
		if (!$currentdir) 
		{
			chomp($currentdir = $_);
		}
	}
	my $code = close(PWDCMD);
	return $currentdir;
}

############################################################################################### 
# checks the server runs in a 64 bits mode 

sub is_64bitEnabledServer {
	my $server = shift ; 
	my $is_64bitMode = 0 ; 
	if ($os eq "HP-UX") {
		my $slapd_file  = "$server${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}pa20_64${PATHSEP}${slapdExecName}" ;
		my $isainfo     = "getconf" ;
		if (-x $slapd_file)
		{
			open(ISAINFO, "${quote}${quote}$isainfo${quote} KERNEL_BITS${quote} 2>&1 |") or die "Could not run program $isainfo: $!";
			sleep(1); # allow some data to accumulate in the pipe
			while (<ISAINFO>) {
				if (/^\s*64\s*$/){
					$is_64bitMode = 1;
				}
			}
			$code = close(ISAINFO);
		}
	}
	elsif (! $isNT) 
	{ 
		my $slapd_file  = "$server${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}64${PATHSEP}${slapdExecName}" ; 
		my $isainfo     = "/bin/isainfo" ; 
		if (-x $slapd_file) 
		{ 
			open(ISAINFO, "${quote}${quote}$isainfo${quote} -b${quote} 2>&1 |") or 
			die "Could not run program $isainfo: $!"; 
			sleep(1); # allow some data to accumulate in the pipe 
			while (<ISAINFO>) 
			{
				if (/^\s*64\s*$/)
				{ 
					  $is_64bitMode = 1; 
				} 
			}
			$code = close(ISAINFO); 
		} 
	}
	return $is_64bitMode ; 
}

sub setup_64bitServer { 
	my $server = shift ; 
	return if ( $isNT ) ; 
	my $dir_64;
	if ($os eq "HP-UX") {
			$dir_64=pa20_64;
	} else
	{
			$dir_64=64;
	}
	if ("$server" eq "${oldDir}"){
			${oldSlapdExecDir}      = "${oldSlapdExecDir}${PATHSEP}${dir_64}${PATHSEP}" ;
			${oldRootServerLib}     = "${oldRootServerLib}${PATHSEP}${dir_64}${PATHSEP}";
	}
}

#############################################3
sub CopySchema {
	my $oldSchemaDir = shift;
	my $migrateSchemaDir = shift;
	my $oldSchemaDir = shift;
	
	my $FilesChanged = "";
	my $AllDiffs     = "";
	my $NoChanges    = "" ;
	my $lineToBegin  = 0 ;

	opendir(SCHEMADIR, $oldSchemaDir) or
	die "Error: could not open migrated config dir $oldSchemaDir: $!";

	if ( ! (-d ${migrateSchemaDir} )) 
	{
		mkdir($migrateSchemaDir,0777) or die "can't create directory $migrateSchemaDir. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $migrateSchemaDir );
	}

	foreach $file (readdir(SCHEMADIR)) 
	{
		if (! exists($stdIncludes{lc($file)})) 
		{
			if ( $AllDiffs eq "")
			{
				printMsg("\n\n------------------------------------------------------------------------\n\n");
				printMsg("Copying the schema files...\n");
			}
			my $newfile = $migrateSchemaDir . $file;
			$AllDiffs .= "\n$file";
			copyBinFile("$oldSchemaDir$file", $newfile);
			printMsg("\n$oldSchemaDir$file copied\n");
		}    
	}
	closedir(SCHEMADIR);
	if ( ! ($AllDiffs eq "") )
	{
#		printMsg("\n------------------------------------------------------------------------\n\n");
	}
}

#############################################################################
sub CopySSL {
	my $oldkeyfile = shift;
	my $oldcertfile = shift;

	my $line = 0;

	# copy the SSL directory
	&copyDir("$oldHome${PATHSEP}ssl","$migrate_dir${PATHSEP}ssl") if (-d "$oldHome${PATHSEP}ssl");

	# copy the cert db and key files
	if ( -d "$oldDir${PATHSEP}alias") 
	{
		$aliasDir = "$migrate_dir${PATHSEP}alias";
		if (! -d $aliasDir) 
		{
			mkdir($aliasDir,0750) or die "can't create directory $aliasDir. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $aliasDir );
		}
		my $keydb         = "$aliasDir${PATHSEP}slapd-$oldname-key3.db"  ;
		my $certdb        = "$aliasDir${PATHSEP}slapd-$oldname-cert7.db" ;

		if ( $oldkeyfile )
		{
			$old_keydb = "$oldDir${PATHSEP}".$oldkeyfile;
		}
		else
		{   
			$old_keydb     = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-key3.db" ;
		}
		if ( $oldcertfile )
		{
			$old_certdb = "$oldDir${PATHSEP}".$oldcertfile;
		}
		else
		{   
			$old_certdb     = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-cert7.db" ;
		}

		if (-f $old_keydb) 
		{
			printMsg("\n\n------------------------------------------------------------------------\n\n");
			printMsg("Copying security files ...\n");
			$line = 1;
			&copyBinFile($old_keydb,$keydb);
			printMsg("\n$old_keydb copied.");
		}
		if (-f $old_certdb) 
		{
			if ( $line == 0 )
			{
				printMsg("\n\n------------------------------------------------------------------------\n\n");
				printMsg("Copying security files ...\n");
				$line = 1;
			}
			&copyBinFile($old_certdb,$certdb);
			printMsg("\n$old_certdb copied.");
		}
		# copy the old password file
		if (-f "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt") 
		{
			if ( $line == 0 )
			{
				printMsg("\n\n------------------------------------------------------------------------\n\n");
				printMsg("Copying security files ...\n");
			}
			&copyBinFile(
				"$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt",
				"$aliasDir${PATHSEP}$type-$oldname-pin.txt");

			printMsg("\n$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt copied.");
		}
	}
}

##############################################################
sub CopyCertmap {
	# backup the old certmap.conf and replace it with the new one
	my $oldCertmap = "$oldDir${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf";
	my $newCertmap = "$migrate_dir${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf"  ;
	if ( -f "$oldCertmap" )
	{
		if ( ! -d "$migrate_dir${PATHSEP}shared${PATHSEP}config" )
		{
			mkdir("$migrate_dir${PATHSEP}shared${PATHSEP}",0750) or die "can't create directory \"$migrate_dir${PATHSEP}shared${PATHSEP}\". \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e "$migrate_dir${PATHSEP}shared${PATHSEP}" );
			mkdir("$migrate_dir${PATHSEP}shared${PATHSEP}config",0750) or die "can't create directory \"$migrate_dir${PATHSEP}shared${PATHSEP}config\". \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e "$migrate_dir${PATHSEP}shared${PATHSEP}config" );
		}

		printMsg("\n\n------------------------------------------------------------------------\n\n");
		printMsg("Copying certmap file ...\n");
		&copyBinFile($oldCertmap,$newCertmap);
		printMsg("\n$oldCertmap copied.");
	}
}
 
##############################################################
sub getInstallPath {
      
    ${dest} = "${migrateHome}${PATHSEP}installpath";
    open(DEST, ">$dest") || die "\nCan't create $dest ($!). \nPlease check you have enough rights to create it \n" if !( -e $dest);
    print DEST $oldDir;
    close(DEST);
}

##############################################################
sub CopyAdm {
	# backup admin-serv/config/adm.conf
	my $oldfile = "$oldDir${PATHSEP}admin-serv${PATHSEP}config${PATHSEP}adm.conf";
	my $newfile = "$migrate_dir${PATHSEP}admin-serv${PATHSEP}config${PATHSEP}adm.conf"  ;
	if ( -f "$oldfile" )
	{
		if ( ! -d "$migrate_dir${PATHSEP}admin-serv${PATHSEP}config" )
		{
			mkdir("$migrate_dir${PATHSEP}admin-serv${PATHSEP}",0750) or die "can't create directory \"$migrate_dir${PATHSEP}admin-serv${PATHSEP}\". \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e "$migrate_dir${PATHSEP}admin-serv${PATHSEP}" );
			mkdir("$migrate_dir${PATHSEP}admin-serv${PATHSEP}config",0750) or die "can't create directory \"$migrate_dir${PATHSEP}admin-serv${PATHSEP}config\". \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e "$migrate_dir${PATHSEP}admin-serv${PATHSEP}config" );
		}

		printMsg("\n\n------------------------------------------------------------------------\n\n");
		printMsg("Copying configuration directory server information file ...\n");
		&copyBinFile($oldfile,$newfile);
		printMsg("\n$oldfile copied.");
	}
}
 
