#
# SunONE Comms Server tool for preparing Directory Server
#   supports DS 5.x and later only
#
# Copyright (c) 2000-2003, Sun Microsystems Inc. All Rights Reserved.
#

$SCHEMA1_MODE = 1;
$SCHEMA2COMPAT_MODE = 1.5;
$SCHEMA2NATIVE_MODE = 2;

########################################
# make sure we are root
########################################
if ($< != 0) {
  print "\nPlease run this tool as root.\n\n";
  exit;
}

########################################
# setup logging, output is LOG file descriptor
########################################
sub log_init
{
  my($t, $stat);
  $t = time;
  $now = localtime($t);
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($t);
  $year += 1900;
  $mon = $mon + 1;
  $datestr = sprintf ("%d%02d%02d%02d%02d%02d", $year, $mon, $mday, $hour, $min, $sec);
  $LOGFILE = "/var/tmp/dssetup_$datestr.log";
  $stat = open(LOG, ">>$LOGFILE");
  if ( $stat == 0 ) {
    log_only ("ERROR: Cannot open $LOGFILE");
    die("ERROR: Cannot open $LOGFILE");
  }
  print LOG "-=-=-=-=-=-=-=-=-=-=-=-=\n";
  print LOG "$now\n";
  print LOG "Running $^X $0 @ARGV\n";
  if ($^O eq "MSWin32") {
    print LOG "  Windows Version was determined to be $windowsversion\n";
  }
}

########################################
# log a message to the screen and the logfile
########################################
sub log_msg
{
  print "$_[0]";
  print LOG "$_[0]";
}

########################################
# log a message to the logfile only
########################################
sub log_only
{
  print LOG "$_[0]";
}

########################################
# print debugging message, if $DEBUG != 0
########################################
sub debug_log
{
  if ( $DEBUG != 0 ) {
    log_msg $_[0];
  }
}

########################################
# print message and abort
########################################
sub abort
{
  log_msg "\n$_[0]\n\n";
  exit 1;
}

########################################
# print warning
########################################
sub warning
{
  log_msg "\n WARNING: $_[0]\n\n";
  exit 1;
}

########################################
# print question and ask whether to continue or not
########################################
sub ContinueYesNo
{
  if ($CmdLineFlag ) {
    return;
  }

  log_msg "Do you want to continue [y]: ";
  $answer = <STDIN>;
  log_only $answer;
  chomp $answer;

  if ( !$answer ) {
    $answer = "y";
  }

  if (!($answer =~ /^y|^Y/)) {
    abort ( "Exiting....");
  }

  return;
}

########################################
# print question and read the answer
########################################
sub ReadAnswer
{
  if ($CmdLineFlag ) {
    return;
  }

  ($question, $default) = @_;

  log_msg "\n$question";

  $answer = <STDIN>;
  chomp $answer;

  # Do not put password in logfile.
  if ($question ne "Password: ") {
    log_only $answer;
  }
  if ( $answer ) {
    return ( $answer );
  } else {
    return ( $default );
  }
}

########################################
# Usage
########################################
sub Usage
{
log_msg <<USAGE;

Usage:
    comm_dssetup.pl  -  without any arguments will be interactive
    comm_dssetup.pl  -i <yes|no> -R <yes|no> -c <Directory Server Root>
             -d <Directory instance> -r <DC tree suffix>
             -u <User/Group suffix> -s <yes|no>
             -D <DirectoryManagerDN> -w <password> -b <yes|no> -t <1|1.5|2>
             -m <yes|no> [-S <path-to-schema-files>]

       -i : yes - Add new indexes
            No  - Do not add
       -R : execute reindexing if new indexes found and -m yes is given
       -c : Directory Server Root pathname. e.g. /var/opt/mps/serverroot
       -d : Directory Server instance subdirectory e.g. "slapd-ketu"
       -r : DC tree suffix e.g. "o=internet"
       -u : User/Group suffix e.g. "o=usergroup"
       -s : yes - update schema, you must have config directory with schema
            files (e.g. schema)
            No  - Do not update
       -D : Directory Manager DN
       -w : Directory Manager password
       -b : yes - config and user directory
            no - config only
       -t : 1 - schema 1
            1.5 - schema 2 compatibility mode
            2   - schema 2 native mode
       -m : yes - modify Directory
            No  - do not modify Directory

Example:
       comm_dssetup.pl

       OR

       comm_dssetup.pl -i yes -R no -c /var/opt/mps/serverroot -d "slapd-ketu"
                      -r "o=internet" -u "o=usergroup" -s yes
                      -D "cn=Directory Manager" -w password -b yes -t 1 -m yes


USAGE

}

########################################
# parse arguments
########################################
sub CmdLineMode
{

  for ($i=0; $i <= $#ARGV; $i++) {
    $arg = $ARGV[$i];

    if ($arg eq "-c") {
      $ServerRoot = $ARGV[++$i];
    } elsif ( $arg eq "-s") {
      $UpdateMsgSchema = $ARGV[++$i];
    } elsif ( $arg eq "-i") {
      $AddIndex = $ARGV[++$i];
    } elsif ( $arg eq "-R") {
      $ReIndex = $ARGV[++$i];
    } elsif ( $arg eq "-d") {
      $DSinstance = $ARGV[++$i];
    } elsif ( $arg eq "-r") {
      $DCSuffix = $ARGV[++$i];
    } elsif ( $arg eq "-u") {
      $UGSuffix = $ARGV[++$i];
    } elsif ( $arg eq "-S") {
      $SchemaDir = $ARGV[++$i];
    } elsif ( $arg eq "-D") {
      $DirectoryManager = $ARGV[++$i];
    } elsif ( $arg eq "-w") {
      $DirectoryPassword = $ARGV[++$i];
    } elsif ( $arg eq "-b") {
      $ConfigUser = $ARGV[++$i];
      if ($ConfigUser =~ /^y|^Y/) {
	$ConfigOnly = 0;
      } else {
	$ConfigOnly = 1;
      }
    } elsif ( $arg eq "-t") {
      $SchemaType = $ARGV[++$i];
    } elsif ( $arg eq "-m") {
      $ModifyDirectory = $ARGV[++$i];
      if ($ModifyDirectory =~ /^yes|^Yes/) {
	$ModifyDirectory = "Yes";
      }
    } else {
      Usage;
      exit;
    }
  }
}

########################################
# print welcome banner
########################################
sub PrintBanner
{

# Update the messages : TODO
log_msg <<EOL_BANNER;

Welcome to the Directory Server preparation tool for
Sun Java(tm) System communication services.
(Version $DSsetupVer Revision $DSsetupRev)

This tool prepares your directory server for use by the
communications services which include Messaging, Calendar and their components.

The logfile is $LOGFILE.

EOL_BANNER

}

########################################
# get directory server instance
########################################
sub GetServerInstance
{
  if ($CmdLineFlag) {
    return;
  }

  log_msg "\nPlease enter the full path to the directory where the Sun ONE\n";
  log_msg "Directory Server was installed.\n";

  # Get the Directory Server Root

  while ( 1 ) {

    $TmpRoot = ReadAnswer ( "Directory server root [$ServerRoot] : ", $ServerRoot );

    if ( -d $TmpRoot ) {
      chdir $TmpRoot;
      $dircnt = 0;

      opendir (DIR, "$TmpRoot");
      while ( defined ( $direntry = readdir DIR ) ) {
	if (( $direntry =~ /^slapd-/ ) &&
	    ( -d $direntry )) {
	  push @instance, $direntry;
	  $dircnt++;
	}
      }
      close DIR;

      if ( $dircnt ge 1 ) {
	$ServerRoot = $TmpRoot;
	last;
      } else {
	log_msg "\nERROR: No directory instances found.\n\n";
	if ( -d "$TmpRoot/bin/slapd" ) {
	  log_msg "Please create a new directory instance and run this tool.\n";
	  chdir $CurrDir;
	}
      }

    } else {
      log_msg "\nERROR: $TmpRoot is not a directory\n\n";
    }
  }

  log_msg "\n\nPlease select a directory server instance from the following list:\n\n";

  $sindex = 0;
  foreach $instance (@instance) {
    $sindex++;
    log_msg "[$sindex]   $instance \n";
  }

  log_msg "\n\nWhich instance do you want [1]: ";

  $answer = <STDIN>;
  chomp $answer;
  log_only $answer;
  if ($answer eq '') {
    $answer = 1;
  }

  if ($answer !~ /^\d+$/) {
    abort ("ERROR: Instance chosen is not valid.");
    exit ;
  }

  if ( $answer <= 0 || $answer > $sindex ) {
    abort ("ERROR: Instance chosen is not valid.");
    exit ;
  }

  $DSinstance = $instance[--$answer];
}

########################################
# get scripts to stop/start the Directory Server
########################################
sub get_startstop_scripts
{
  my($serv_dir);
  if (!$ConfigDir || $ConfigDir !~ /config[\/\\]*$/) {
    abort("don't know how to restart server. please restart the server");
  }

  $serv_dir = $ConfigDir;
  $serv_dir =~ s/config[\/\\]?$//;
  if ($serv_dir !~ /slapd-.*$/) {
    abort("ERROR: Don't know how to restart server. please restart the server");
  }
  $_ = $serv_dir;
  m/slapd-.*[\/\\]*$/;
  $serv_name = $&;
  $serv_name =~ s/[\/\\]*$//;

  $stop = sprintf("%sstop-slapd", $serv_dir);
  $start = sprintf("%sstart-slapd", $serv_dir);

  # if stop script exists assume unix. otherwise assume nt
  $stop = "net stop $serv_name" if (!-x $stop);
  $start = "net start $serv_name" if (!-x $stop);
}

########################################
# shut down directory server
########################################
sub shutdown_server
{
  # execute the stop script
  #log_only $stop;
  #$exit_status = system($stop) / 256;
  #sleep(5);
  print CMD "$stop\n";
  print CMD "if [ \$? != 0 ]; then\n";
  print CMD "  echo \"Stopping the Directory server failed\"\n";
  print CMD "  echo \"exit 1\"\n";
  print CMD "fi\n";
}

########################################
# start up directory server
########################################
sub start_server
{
  # execute the start script
#   log_only $start;
#   $exit_status = system($start) / 256;
#   sleep(5);

#   if ($exit_status != 0) {
#     warning "The server may not have restarted properly. Please restart it manually\n";
#     exit 1;
#   }

  print CMD "$start\n";
  print CMD "if [ \$? != 0 ]; then\n";
  print CMD "  echo \"The server may not have restarted properly\"\n";
  print CMD "  echo \"Please restart it manually\"\n";
  print CMD "fi\n";
}

########################################
# Add PAB, u/g suffix and DC tree DN, the naming context are already created
########################################
sub add_ldif
{
  my($found);
  chdir $CmdDir;

  # create the o=pab suffix
  #
  $found = 0;
  foreach $_ (`$SRCHCMD $LdapOpt -b "o=pab" -s base objectclass=top 2>&1`) {
    if ($_ =~ /o=pab/) {
      $found = 1;
      last;
    }
  }

  if (! $found ) {
    log_msg "  Generating ldif to create PAB DN (o=pab)\n";
    print LDIF <<PAB_SUFFIX;

########################################
# dn: o=pab
# Create the o=pab DN
########################################
dn: o=pab
changetype: add
objectclass: top
objectclass: organization
PAB_SUFFIX
  }

  # create the $PiSuffix suffix
  #
  $found = 0;
  foreach $_ (`$SRCHCMD $LdapOpt -b "$PiSuffix" -s base objectclass=top 2>&1`) {
    if ($_ =~ /$PiSuffix/) {
      $found = 1;
      last;
    }
  }

  if (! $found ) {
    log_msg "  Generating ldif to create Pi DN ($PiSuffix)\n";
    print LDIF <<PI_SUFFIX;

########################################
# dn: $PiSuffix
# Create the $PiSuffix DN
########################################
dn: $PiSuffix
changetype: add
objectclass: top
objectclass: organization
PI_SUFFIX
  }

  # create the osi suffix
  #
  for ($i = $#ugcomps; $i >= 0; $i--) {
    $dn = "";
    for ($j = $i; $j <= $#ugcomps; $j++) {
      $seg = $ugcomps[$j];
      $dn .= $seg;
      if ($j != $#ugcomps) {
        $dn .= ", ";
      }
    }

    $found = 0;
    foreach $_ (`$SRCHCMD $LdapOpt -b "$dn" -s base objectclass=top 2>&1`) {
      if ($_ =~ /$dn/) {
        $found = 1;
        last;
      }
    }

    if (! $found) {
      log_msg "  Generating ldif to create user/group path DN ($dn)\n";
      print LDIF <<UGPATH_DN;

########################################
# dn: $dn
# Create the dn for the user/group suffix
########################################
dn: $dn
changetype: add
objectclass: top
UGPATH_DN
      if ($dn =~ /^[cC]=/) {
        print LDIF "objectclass: country\n";
      } elsif ($dn =~ /^[lL]=/) {
        print LDIF "objectclass: locality\n";
      } elsif ($dn =~ /^[oO]=/) {
        print LDIF "objectclass: organization\n";
      } elsif ($dn =~ /^[oO][uU]=/) {
        print LDIF "objectclass: organizationalunit\n";
      } elsif ($dn =~ /^[dD][cC]=/) {
        print LDIF "objectclass: domain\n";
      }
    }
  }

  if ($SchemaType == $SCHEMA1_MODE) {
    # create the dc suffix
    #
    for ($i = $#dccomps; $i >= 0; $i--) {
      $dn = "";
      for ($j = $i; $j <= $#dccomps; $j++) {
        $seg = $dccomps[$j];
        $dn .= $seg;
        if ($j != $#dccomps) {
          $dn .= ", ";
        }
      }

      $found = 0;
      foreach $_ (`$SRCHCMD $LdapOpt -b "$dn" -s base objectclass=top 2>&1`) {
        if ($_ =~ /$dn/) {
          $found = 1;
          last;
        }
      }

      if (! $found) {
	log_msg "  Generating ldif to create DC tree path DN ($dn)\n";
        print LDIF <<DCPATH_DN;

########################################
# dn: $dn
# Create the dn for the DC tree suffix
########################################
dn: $dn
changetype: add
objectclass: top
DCPATH_DN
        if ($dn =~ /^[cC]=/) {
          print LDIF "objectclass: country\n";
        } elsif ($dn =~ /^[lL]=/) {
          print LDIF "objectclass: locality\n";
        } elsif ($dn =~ /^[oO]=/) {
          print LDIF "objectclass: organization\n";
        } elsif ($dn =~ /^[oO][uU]=/) {
          print LDIF "objectclass: organizationalunit\n";
        } elsif ($dn =~ /^[dD][cC]=/) {
          print LDIF "objectclass: domain\n";
        }
      }
    }
  }

  chdir $CurrDir;
}

########################################
#
# Script starts here
#   main program
#
########################################

$DEBUG=0;
log_init

# Default values
$InstallerSuffix = "o=comms-config";
$CommServersDN = "cn=CommServers";
$DSsetupVer = "6.1";
$DSsetupRev = "0.2";
if ($DSsetupRev eq "") {
  $DSsetupRev = "0";
}
if ($^O eq "MSWin32") {
  $CurrDir = Win32::GetCwd();
  $delim = '\\';
  $ServerRoot = "C:\\Sun\Servers";
} else {
  $CurrDir = `pwd`;
  chomp $CurrDir;
  $delim = '/';
  $ServerRoot = "/var/opt/mps/serverroot";
}
$DCSuffix   = "o=internet";
$UGSuffix   = "o=usergroup";
$PABSuffix   = "o=pab";
$PiSuffix   = "o=PiServerDb";
$UpdateMsgSchema   = "yes";
$AddIndex          = "yes";
$ReIndex          = "yes";
$ConfigDir  = "";
$LdapPort   = 389;
$DirectoryManager = "cn=Directory Manager";
$ConfigUser = "Yes";
$ConfigOnly = 0;
$ModifyDirectory = "Yes";

# determine script directory, trailing delimiter is part of the variable
$pos = rindex($0,$delim);
if ($pos == -1) {
  $ScriptDir = "." . $delim;
} else {
  $ScriptDir = substr($0,0,$pos + 1);
}

$SchemaDir   = $ScriptDir . "schema";
chdir $SchemaDir;
if ($^O eq "MSWin32") {
  $SchemaDir = Win32::GetCwd();
} else {
  $SchemaDir = `pwd`;
  chomp $SchemaDir;
}
chdir $CurrDir;


$CmdLineFlag   = 0;

# New indexes to be added per respective database
%UGIndexes = (  inetMailGroupStatus => 'pres,eq',
                inetUserStatus => 'pres,eq',
                mail => 'pres,eq,approx,sub',
                mailAlternateAddress => 'pres,eq,approx,sub',
                mailEquivalentAddress => 'pres,eq,approx,sub',
                mailHost => 'pres,eq,approx,sub',
                mailUserStatus => 'pres,eq',
                member => 'eq',
                ou => 'pres',
                cosspecifier => 'pres',
                createtimestamp => 'eq',
                modifytimestamp => 'eq' );

%DCIndexes = (  inetDomainBaseDN => 'pres,eq',
                inetCanonicalDomainName => 'pres,eq',
                inetDomainStatus => 'pres,eq',
                mailDomainStatus => 'pres,eq',
                mailRoutingHosts => 'pres,eq,approx,sub',
                dc => 'pres',
                createtimestamp => 'eq',
                modifytimestamp => 'eq' );

%PABIndexes = ( memberOfManagedGroup => 'pres,eq',
                memberOfPAB => 'pres,eq',
                memberOfPABGroup => 'pres,eq',
                un => 'eq',
                createtimestamp => 'eq',
                modifytimestamp => 'eq',
		icsCalendar => 'pres,eq,approx,sub',
                icsCalendarOwned => 'pres,eq,approx,sub');

%PiIndexes = (  displayname => 'pres,eq,sub',
                MemberOfPiBook => 'eq',
                MemberofPiGroup => 'eq');

if ( $ARGV[0] ) {

  # Command Line mode
  $CmdLineFlag = 1;

  if ( $#ARGV ne 23 && $#ARGV ne 25 ) {
    Usage;
    exit;
  }

  CmdLineMode;
}

PrintBanner;

if (!$CmdLineFlag) {
  ContinueYesNo;

  GetServerInstance;
}

$ConfigDir = "$ServerRoot/$DSinstance/config";
$CmdDir = "$ServerRoot/shared/bin";

if (!$CmdLineFlag) {
  $DirectoryManager = ReadAnswer("Please enter the directory manager DN [$DirectoryManager]: ", $DirectoryManager);
}
# strip quotes around Directory Manager if any
if ($DirectoryManager =~ /^\".+\"$/) {
  $DirectoryManager =~ s/^\"(.+)\"$/$1/;
}
if (!$CmdLineFlag) {
  if ($^O ne "MSWin32") {
    log_only "stty -echo";
    system ("stty -echo");
  }
  $DirectoryPassword = ReadAnswer("Password: ", $DirectoryPassword );
  if ($^O ne "MSWin32") {
    log_only "stty echo";
    system ("stty echo");
  }
}
# have to have a password, otherwise an anonymous bind is done, which I
# do not want.
if ( $DirectoryPassword eq "" ) {
  # invalid to have no password
  log_only "\nERROR: You must supply a Directory Manager password.\n";
  die "\nERROR: You must supply a Directory Manager password.\n";
}
# 
# Determine the SLAPD port and host
#
my($cfg) = "$ConfigDir/dse.ldif";
open(SLAPD, "<$cfg") || abort("ERROR: Unable to open $cfg");
while (defined($_ = <SLAPD>)) {
  if ($_ =~ /^nsslapd-port/) {
    chomp;
    ($tmp,$LdapPort) = split(/: /, $_, 2);
  }
  if ($_ =~ /^nsslapd-localhost/) {
    chomp;
    ($tmp,$LdapHost) = split(/: /, $_, 2);
  }
  if ($_ =~ /^nsslapd-listenhost/) {
    chomp;
    ($tmp,$ListenHost) = split(/: /, $_, 2);
  }
}
close(SLAPD);

if ( defined($ListenHost) ){
  $LdapHost = $ListenHost;
}

# When these commands are executed the current directory will be $CmdDir
if ($^O eq "MSWin32") {
  $SRCHCMD = ".\\ldapsearch";
  $MODCMD = ".\\ldapmodify";
} else {
  $SRCHCMD = "./ldapsearch";
  $MODCMD = "./ldapmodify";
  $OrigDirectoryPasswd = $DirectoryPassword;
  $DirectoryPassword =~ s/\$/\\\$/g;
  $DirectoryPassword =~ s/\`/\\\`/g;
}
$LdapOpt = "-D \"$DirectoryManager\" -w \"$DirectoryPassword\" -p $LdapPort -h $LdapHost";
$LdapOptNoCred = "-p $LdapPort -h $LdapHost";

($DSversion, $DSversionMinor) = check_ds_version();
log_msg "\nDetected DS version $DSversion.$DSversionMinor\n";
if ($DSversion == 0) {
  log_only "Please correct the problem and re-run this script. $!\n";
  die "Please correct the problem and re-run this script.\n";
}
if ($DSversion < 5) {
  log_only "Only Directory Server versions 5.x and above are supported.\n";
  die "Only Directory Server versions 5.x and above are supported.\n";
}
($StoredDssetupVer, $StoredDssetupRev, $StoredDcsuffix, $StoredUgsuffix, $StoredSchematype) = GetStoredDssetupData();
if ( ($StoredDssetupVer > $DSsetupVer) ||
     ( ($StoredDssetupVer == $DSsetupVer) &&
       ($StoredDssetupRev > $DSsetupRev)) ) {
  log_msg "\nWarning: This version of comm_dssetup.pl ($DSsetupVer $DSsetupRev) is older than the currently installed version ($StoredDssetupVer $StoredDssetupRev)\n";
  if (!$CmdLineFlag) {
    ContinueYesNo;
  }
}
if ( ($StoredDssetupVer == $DSsetupVer) &&
     ($StoredDssetupRev == $DSsetupRev)) {
  log_msg "\nWarning: This version of comm_dssetup.pl ($DSsetupVer $DSsetupRev) has been run previously ($StoredDssetupVer $StoredDssetupRev), you do not need to run it again unless you wish to specify different parameters to this program\n";
  if (!$CmdLineFlag) {
    ContinueYesNo;
  }
}

# Get config and users/groups indicator
if (!$CmdLineFlag) {
  $ConfigUser = ReadAnswer ("Will this directory server be used for users/groups [$ConfigUser]: ", $ConfigUser );
  if ( $ConfigUser =~ /^y|^Y/ ) {
    $ConfigOnly = 0;
  } else {
    $ConfigOnly = 1;
  }
}

if (!$ConfigOnly) {
  # Get User/Group root suffix
  if (!$CmdLineFlag) {
    if ( $StoredUgSuffix ne "" ) {
      $UGSuffix = $StoredUgSuffix;
    }
    $UGSuffix = ReadAnswer ("Please enter the Users/Groups base suffix [$UGSuffix] : ", $UGSuffix ) if (!$ConfigOnly);
  }
  $UGSuffix = convert2DN($UGSuffix);
  ($UGSuffixFound,$db) = has_naming_context($UGSuffix);
  debug_log "For $UGSuffix found = $UGSuffixFound db = $db \n";

  # Determine default schema type
  if ( $UGSuffixFound ) {
    $DefSchemaType = get_schema_type($UGSuffix);
    if ( ($UGSuffix eq $StoredUgsuffix) &&
	 ($DefSchemaType == $SCHEMA2NATIVE_MODE) &&
	 ($StoredSchemaType == $SCHEMA2COMPAT_MODE) ) {
      $DefSchemaType = $SCHEMA2COMPAT_MODE;
    }
  } else {
    $DefSchemaType = $SCHEMA1_MODE;
  }

  # Ask User for schema type
  while (1) {
    log_msg "There are 3 possible schema types:\n";
    log_msg "  1   - schema 1 for systems with iMS 5.x data\n";
    log_msg "  1.5 - schema 2 compatibility for systems with iMS 5.x data\n";
    log_msg "        that has been converted with commdirmig\n";
    log_msg "  2   - schema 2 native for systems using Identity Server\n";
    if (!$CmdLineFlag) {
      $SchemaType = ReadAnswer ("Please enter the Schema Type ($SCHEMA1_MODE, $SCHEMA2COMPAT_MODE, $SCHEMA2NATIVE_MODE) [$DefSchemaType]: ", $DefSchemaType );
    }
    if ( ($SchemaType ne "$SCHEMA1_MODE") &&
	 ($SchemaType ne "$SCHEMA2COMPAT_MODE") &&
	 ($SchemaType ne "$SCHEMA2NATIVE_MODE") ) {
      log_msg "Invalid choice, please try again\n";
      if (!$CmdLineFlag) {
	next;
      } else {
	exit(1);
      }
    }

    # Validate schema type choice
    if ($SchemaType != $DefSchemaType) {
      if (! $UGSuffixFound ) {
	# new u/g suffix, only choice is schema 1 or schema 2
	# I set DefSchemaType=1, so the user must have chosen schema 2
	if ( $SchemaType == $SCHEMA2NATIVE_MODE ) {
	  log_msg "To use Schema 2 mode with a non-existent user/group suffix, You must first install Identity Server\n";
	  abort "Rerun this program once you have installed Identity Server";
	} else {		# SCHEMA2COMPAT_MODE
	  log_msg "Schema 2 compatibility mode can only be selected for an existing user/group suffix\n";
	  abort "Please rerun this script with an existing user/group suffix";
	}
      } else {
	if ( $DefSchemaType == $SCHEMA1_MODE ) {
	  log_msg "You must first install Identity Server specifying this existing suffix (DIT)\n";
	  abort "Rerun this program once you have installed Identity Server";
	}
	# all other cases
	log_msg "You have specified a schema type different from the recommended default\n";
	if (!$CmdLineFlag) {
	  $ans = ReadAnswer ("Are you sure? [n]: ", "n" );
	  if ($ans =~ /^y|^Y/) {
	  } else {
	    next;
	  }
	}
      }
    }
    last;
  }

  # Get DC root
  if ( ($SchemaType == $SCHEMA1_MODE) ||
       ($SchemaType == $SCHEMA2COMPAT_MODE)) {
    while (1) {
      if ( ($StoredDcsuffix ne "") &&
	   ($StoredSchemaType == $SCHEMA1_MODE) ) {
	$DCSuffix = $StoredDcsuffix;
      }
      if (!$CmdLineFlag) {
	$DCSuffix = ReadAnswer ("Please enter the DC Tree base suffix [$DCSuffix]: ", $DCSuffix );
      }
      $DCSuffix = convert2DN($DCSuffix);
      # Do sanity check for schema2 compat mode. DC tree must already exist
      if ($SchemaType == $SCHEMA2COMPAT_MODE) {
	chdir $CmdDir;
	$found = 0;
	foreach $_ (`$SRCHCMD $LdapOpt -b "$DCSuffix" '(&(objectclass=inetDomain)(inetDomainBaseDN=*$UGSuffix))' 2>&1`) {
	  if ($_ =~ /inetDomainBaseDN/i) {
	    $found = 1;
	    last;
	  }
	}
	chdir $CurrDir;
	if (! $found ) {
	  log_msg "I did not locate an existing DC tree structure under this suffix. You must have an existing DC tree structure for Schema 2 Compatibility mode\n";
	  if (!$CmdLineFlag) {
	    next;
	  } else {
	    exit(2);
	  }
	}
      }
      last;
    }
  } else {
    $DCSuffix = $UGSuffix;
  }
}				# ! $ConfigOnly

# Ask for schema update
if (!$CmdLineFlag) {
  $UpdateMsgSchema = ReadAnswer ("Do you want to update the schema files [$UpdateMsgSchema]: ", $UpdateMsgSchema );

  # Ask for adding new indexes
  $AddIndex = ReadAnswer ("Do you want to configure new indexes [$AddIndex]: ", $AddIndex ) if (!$ConfigOnly);
  if ($AddIndex =~ /^y|^Y/ && !$ConfigOnly) {
    # Ask for Reindexing new indexes
    $ReIndex = ReadAnswer ("Do you want to Reindex the new indexes now [$ReIndex]: ", $ReIndex ) if (!$ConfigOnly);
  }

}

log_msg <<EOL_SUMMARY;

Here is a summary of the settings that you chose:

  Server Root                        : ${ServerRoot}
  Server Instance                    : ${DSinstance}
  Users/Groups Directory             : ${ConfigUser}
  Update Schema                      : ${UpdateMsgSchema}
  Schema Type                        : ${SchemaType}
EOL_SUMMARY
if (!$ConfigOnly) {
  log_msg <<EOL_SUMMARY2;
  DC Root                            : ${DCSuffix}
  User/Group Root                    : ${UGSuffix}
  Add New Indexes                    : ${AddIndex}
EOL_SUMMARY2
}
if ($AddIndex =~ /^y|^Y/ && !$ConfigOnly) {
  log_msg <<EOL_SUMMARY4;
  ReIndex New Indexes Now            : ${ReIndex}
EOL_SUMMARY4
}
log_msg <<EOL_SUMMARY3;
  Directory Manager DN               : ${DirectoryManager}

EOL_SUMMARY3

log_msg "\nNow ready to generate a shell script and ldif file to modify the Directory.\n";
log_msg "No changes to the Directory Server will be made this time.\n";

ContinueYesNo;

log_msg "Generating files...\n";

# Create ldif file containing all the changes to be made
$LDIF_FILE = "/var/tmp/dssetup_$datestr.ldif";
$stat = open(LDIF, ">>$LDIF_FILE");
if ( $stat == 0 ) {
  log_only ("ERROR: Cannot open $LDIF_FILE");
  die("ERROR: Cannot open $LDIF_FILE");
}
print LDIF "# This Ldif file contains the changes to be made to the Directory\n";
print LDIF "# Run it like this:\n";
print LDIF "#   cd $CmdDir\n";
print LDIF "#   $MODCMD $LdapOptNoCred -D binddn -w passwd -c -f $LDIF_FILE\n";

# Create the batch file containing the commands to execute
$CMD_FILE = "/var/tmp/dssetup_$datestr.sh";
$stat = open(CMD, ">>$CMD_FILE");
if ( $stat == 0 ) {
  log_only ("ERROR: Cannot open $CMD_FILE");
  die("ERROR: Cannot open $CMD_FILE");
}
create_preamble();

$ugcomps[0] = $UGSuffix;
$dccomps[0] = $DCSuffix;

chdir $CurrDir;

get_startstop_scripts;

if ($UpdateMsgSchema =~ /^y|^Y/) {
  # Stop slapd
  print CMD "\necho Stopping Directory Server\n";
  shutdown_server;

  print CMD "\necho Updating Schema files...\n";
  copy_schema_ldifs();

  print CMD "\necho Starting Directory Server\n";
  start_server;
}

print CMD "echo Applying ldif file $LDIF_FILE \n";
print CMD "cd $CmdDir\n";
print CMD "$MODCMD $LdapOptNoCred -D \"\$BINDDN\" \$pflag \"\$parg\" -c -e $LDIF_FILE.rej -f $LDIF_FILE\n";
print CMD "stat=\$?\n";
print CMD "echo Done Applying ldif file $LDIF_FILE, rejects to $LDIF_FILE.rej status = \$stat \n";
print CMD "if [ \$stat -ne 0 ]; then\n";
print CMD "  echo \"WARNING: ldapmodify returned \$stat\"\n";
print CMD "fi\n";

if (!$ConfigOnly) {
  log_msg "\nChecking to see if Suffixes need to be added\n";
  print LDIF "\n";
  print LDIF "#--------------------------------------------------\n";
  print LDIF "# This section creates naming context if required\n";
  print LDIF "#--------------------------------------------------\n";
  $ugdb = create_naming_context($UGSuffix);
  if ($SchemaType == $SCHEMA1_MODE) {
    $dcrootdb = create_naming_context($DCSuffix)
  }
  $pabdb= create_naming_context($PABSuffix);
  $pidb= create_naming_context($PiSuffix);
  log_msg "\nChecking to see that uid uniqueness plugins are turned off\n";
  if (disable_plugin("uid uniqueness")) {
    print CMD "\necho Restarting Directory Server\n";
    shutdown_server;
    start_server;
  }
}

if ($AddIndex =~ /^y|^Y/ && !$ConfigOnly) {
  log_msg "\nAdding indexes\n";
  print LDIF "\n";
  print LDIF "#--------------------------------------------------\n";
  print LDIF "# This section creates indexes if required\n";
  print LDIF "#--------------------------------------------------\n";
  # Now, create the additional indexes for iMS 5.x
  #
  log_msg "  Adding Indexes for User/group Tree (backend:$ugdb)\n";
  create_indexes($ugdb, %UGIndexes);
  if ($SchemaType == $SCHEMA1_MODE) {
    log_msg "  Adding Indexes for DC Tree (backend:$dcrootdb)\n";
    create_indexes($dcrootdb, %DCIndexes)
  }
  log_msg "  Adding Indexes for PAB Tree (backend:$pabdb)\n";
  create_indexes($pabdb, %PABIndexes);
  log_msg "  Adding Indexes for New PAB Tree (backend:$pidb)\n";
  create_indexes($pidb, %PiIndexes);
}

if (!$ConfigOnly) {
  log_msg "\nChecking to see if DN needs to be created for suffixes\n";
  print LDIF "\n";
  print LDIF "#--------------------------------------------------\n";
  print LDIF "# This section creates the DN for each suffix if required\n";
  print LDIF "#--------------------------------------------------\n";
  add_ldif;

  # Create search templates if they do no exist
  if ( ($SchemaType == $SCHEMA2NATIVE_MODE) ||
       ($SchemaType == $SCHEMA2COMPAT_MODE)) {

    log_msg "Generating ldif for Creating Search Templates\n";
    print LDIF "\n";
    print LDIF "#--------------------------------------------------\n";
    print LDIF "# This section creates the search templates if they do not exist\n";
    print LDIF "#--------------------------------------------------\n";
    print LDIF <<COMPAT_SEARCH_TEMPLATE;

########################################
# dn: ou=BasicCompatDomainSearch,ou=SearchTemplates,ou=templates,ou=default,
#  ou=GlobalConfig,ou=1.0,ou=DAI,ou=services,$UGSuffix
# Domain Search Template for Schema 2 Compatibility mode
# creates dn if it does not exist. If it exists assume all the attributes
#  are correct
########################################
dn: ou=BasicCompatDomainSearch,ou=SearchTemplates,ou=templates,ou=default,
 ou=GlobalConfig,ou=1.0,ou=DAI,ou=services,$UGSuffix
changetype: add
objectClass: top
objectClass: sunServiceComponent
ou: BasicCompatDomainSearch
sunServiceID: SearchUmsObjects
sunKeyValue: name=BasicOrganizationSearch
sunKeyValue: inetDomainSearchFilter=(&(objectclass=sunManagedOrganization)
 (|(sunPreferredDomain=%V)(associatedDomain=%V))
sunKeyValue: attrs=objectclass
sunKeyValue: attrs=ou
sunKeyValue: attrs=inetDomainStatus
sunKeyValue: rfc2247Flag=true
sunKeyValue: baseDN=$DCSuffix
COMPAT_SEARCH_TEMPLATE

    print LDIF <<NATIVE_SEARCH_TEMPLATE;

########################################
# dn: ou=BasicDomainSearch,ou=SearchTemplates,ou=templates,ou=default,
#  ou=GlobalConfig,ou=1.0,ou=DAI,ou=services,$UGSuffix
# Domain Search Template for Schema 2 Native mode
# creates dn if it does not exist. If it exists assume all the attributes
#  are correct
########################################
dn: ou=BasicDomainSearch,ou=SearchTemplates,ou=templates,ou=default,
 ou=GlobalConfig,ou=1.0,ou=DAI,ou=services,$UGSuffix
changetype: add
objectClass: top
objectClass: sunServiceComponent
ou: BasicOrganizationSearch
sunServiceID: SearchUmsObjects
sunKeyValue: name=BasicDomainSearch
sunKeyValue: inetDomainSearchFilter=(&(objectclass=sunManagedOrganization)
 (|(sunPreferredDomain=%V)(associatedDomain=%V))
sunKeyValue: attrs=objectclass
sunKeyValue: attrs=ou
sunKeyValue: attrs=inetDomainStatus
sunKeyValue: baseDN=$DCSuffix
NATIVE_SEARCH_TEMPLATE
    }

}

log_msg "\nGenerating ldif for installer metadata\n";
print LDIF "\n";
print LDIF "#--------------------------------------------------\n";
print LDIF "# This section creates the installer metadata\n";
print LDIF "#--------------------------------------------------\n";
create_installer_schema();

close (LDIF);
close (CMD);
chmod 0755, $CMD_FILE;

log_msg "The following files have been created:\n";
log_msg "   $CMD_FILE\n";
log_msg "   $LDIF_FILE\n\n";
log_msg "Running $CMD_FILE will make changes to the Directory\n";
log_msg "  You can run this file now or at a later time\n";
log_msg "Ready to execute the script now.\n\n";
ContinueYesNo;

if ( $ModifyDirectory eq "Yes" ) {
  # Create password file
  $PW_FILE = "/var/tmp/dssetup_$datestr.pw";
  $stat = open(PW, ">>$PW_FILE");
  if ( $stat == 0 ) {
    log_only ("ERROR: Cannot create $PW_FILE");
    die("ERROR: Cannot create $PW_FILE");
  }
  print PW "$OrigDirectoryPasswd\n";
  close (PW);
  chmod 0400, $PW_FILE;
  log_msg "Running $CMD_FILE -D \'$DirectoryManager\' -j $PW_FILE \n";
  open(TMPFD, "$CMD_FILE -D \'$DirectoryManager\' -j $PW_FILE 2>&1|");
  while (<TMPFD>) {
    print $_;
    print LOG $_;
  }
  close(TMPFD);
  unlink $PW_FILE;
}

log_msg "Successful Completion. Consult $LOGFILE for details\n";
exit 0;

########################################
# Determine the version of the Directory Server
#   returns the major and minor version
#   returns 0 for the major version if an error occurs.
########################################
sub check_ds_version
{
  my($major, $minor);

  chdir $CmdDir;
  foreach $_ (`$SRCHCMD $LdapOpt -b "cn=monitor" -s base objectclass=top version 2>&1`) {
    if ($_ =~ /^version.*\//) {
      ($major,$minor) = /^version.*\/(\d+)\.(\d+).*/;
      debug_log "DEBUG DS $_ : major=$major minor=$minor\n";
      last;
    } elsif ($_ =~ /^ldap_simple_bind/) {
      $major = 0;
      $minor = 0;
      if ($_ =~ /Can\'t connect/i) {
        log_msg "Directory server appears not running or not listening to port $LdapPort.\n";
      } elsif ($_ =~ /invalid credentials/i) {
        log_msg "The entered password for DN $DirectoryManager is incorrect.\n";
      } else {
        $_ =~ s/^ldap_simple_bind: //i;
        log_msg "Error: $_.\n";
      }
      last;
    }
  }
  chdir $CurrDir;
  return $major, $minor;
}

########################################
# Updates the schema
########################################
sub copy_schema_ldifs
{
  my($Cmd);

  if ($^O eq "MSWin32") {
    $Cmd = "copy $SchemaDir\\*.ldif $ServerRoot\\$DSinstance\\config\\schema"; 
  } else {
    $Cmd = "cp $SchemaDir/*.ldif $ConfigDir/schema"; 
  }
#  log_msg $Cmd;
  print CMD "$Cmd\n";
}

########################################
# Determine if the naming context exists
# returns two values
#  1st value - 1 if found, 0 if not found
#  2nd value - if found the backend db associated with the suffix
########################################
sub has_naming_context
{
  my($suffix) = @_;
  my($suffix_found,$prop,$suffixdb,@result,@arr);

  chdir $CmdDir;
  $suffix_found = 0;
  if ($^O eq "MSWin32") {
    open (SRCH, "$SRCHCMD $LdapOpt -b \"cn=mapping tree,cn=config\" \"(cn=\\\"$suffix\\\")\" 2>&1 |");
  } else {
    open (SRCH, "$SRCHCMD $LdapOpt -b \"cn=mapping tree,cn=config\" '(cn=\"$suffix\")' 2>&1 |");
  }
  @result = <SRCH>;
  close (SRCH);
  foreach (@result) {
    if ($_ =~ /^nsslapd-backend/) {
      chomp;
      ($prop,$suffixdb) = split(/: /, $_, 2);
      $suffix_found = 1;
      last;
    }
  }
  chdir $CurrDir;
  return $suffix_found, $suffixdb;
}

########################################
# Determine if the naming context exists, if not, create it
########################################
sub create_naming_context
{
  my($suffix) = @_;
  my($suffix_found,@arr,$suffixdb);

  ($suffix_found,$suffixdb) = has_naming_context($suffix);

  chdir $CmdDir;
  if (! $suffix_found ) {
    @arr = split(/,/, $suffix);
    @arr = split(/=/, $arr[0]);
    $suffixdb = "$arr[1]db2";

    log_msg "  Generating ldif for Adding suffix $suffix\n";
    print LDIF <<NAMING_MOD;

########################################
# dn: cn="$suffix",cn=mapping tree,cn=config
# Create the naming context for $suffix
#
# Note that DS 5.1 seems to add two cn values, one with double quotes and
#   one without.
########################################
dn: cn="$suffix",cn=mapping tree,cn=config
changetype: add
objectclass: top
objectclass: extensibleObject
objectclass: nsMappingTree
nsslapd-state: backend
nsslapd-backend: $suffixdb
cn: "$suffix"

########################################
# dn: cn="$suffix",cn=mapping tree,cn=config
# Create the backend database name for $suffix.
# This is part of creating the naming context
########################################
dn: cn=$suffixdb,cn=ldbm database,cn=plugins,cn=config
changetype: add
objectclass: extensibleObject
objectclass: nsBackendInstance
nsslapd-suffix: $suffix
NAMING_MOD
  }
  chdir $CurrDir;
  return $suffixdb;
}

########################################
# enable a plugin
#   currently not used
########################################
sub enable_plugin
{
  return modify_plugin(@_, "on");
}

########################################
# disable a plugin
########################################
sub disable_plugin
{
  return modify_plugin(@_, "off");
}

########################################
# modify a plugin
#   called by disable_plugin and enable_plugin
########################################
sub modify_plugin
{
  my($plugin, $state) = @_;
  my($plugin_found, $plugin_touched) = (0, 0);

  chdir $CmdDir;
  foreach $_ (`$SRCHCMD $LdapOpt -b "cn=$plugin,cn=plugins,cn=config" -s base objectclass=top nsslapd-pluginEnabled 2>&1`) {
    if ($_ =~ /^nsslapd-pluginEnabled/) {
      ($curr_plugin_state) = /.+:\s+(\w+)/;
      $plugin_found = 1;
      last;
    }
  }

  if ($plugin_found && ($curr_plugin_state ne $state)) {
    log_msg "  Generating ldif to Turn $plugin plugin $state\n";
    print LDIF <<PLUGIN_MOD;

# Turn $plugin plugin $state
dn: cn=$plugin,cn=plugins,cn=config
changetype: modify
replace: nsslapd-pluginEnabled
nsslapd-pluginEnabled: $state
PLUGIN_MOD
    $plugin_touched = 1;
  }
  chdir $CurrDir;
  return $plugin_touched;
}

########################################
# Create indexes
#   Checks are done to see if the indexes already exist, and whether the
#   index types already exist.
#   Uses $ReIndex flag for user input on whether to invoke db2index.pl
#   Note that db2index is called with the -t switch specifying the index
#   otherwise the entire suffix is re-indexed, and DS will be in readonly
#   mode for duration of re-indexing.
#  The Directory creates some default index types for certain indexes when you
#  create the naming context. This is stored under:
# dn: cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config
########################################
sub create_indexes
{
  my($db, %indexes) = @_; 
  my($index,@indextypes,$type,$needReindex);

  chdir $CmdDir;

  # loop thru the list of indexes
  #
  foreach $index ( keys %indexes ) {
    log_msg "    Checking indexes for $index\n";
    $needReindex = 0;
    @indextypes = split(/,/, $indexes{$index});
    # detect if index is already present or not. if present, save the types
    $index_found = 0;
    my($currindex);
    foreach $_ (`$SRCHCMD $LdapOpt -b "cn=index,cn=$db,cn=ldbm database,cn=plugins,cn=config" cn=$index nsIndexType 2>&1`) {
      if ($_ =~ /No such object/i) {
	last;
      }
      $index_found = 1;
      if ($_ =~ /^nsIndexType/) {
        ($idx) = /.+:\s+(\w+)/;
        if (! defined($currindex) ) {
          $currindex = $idx
        } else {
          $currindex .= ",$idx"
        }
        log_only "      index type $idx found currindex=$currindex\n";
      }
    }
    foreach $_ (`$SRCHCMD $LdapOpt -b "cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config" cn=$index nsIndexType 2>&1`) {
      if ($_ =~ /^nsIndexType/) {
        ($idx) = /.+:\s+(\w+)/;
        if (! defined($currindex) ) {
          $currindex = $idx
        } elsif (index($currindex, $idx) == -1) {
          $currindex .= ",$idx"
        }
        log_only "      default index type $idx found currindex=$currindex\n";
      }
    }

    if ( ! $index_found ) {
      $needReindex = 1;
      # index does not exist, add the index and all its types
      log_msg "      Indexes for attr $index does not exist\n";
      print LDIF <<DN_ADD;

########################################
# dn: cn=$index,cn=index,cn=$db,
#  cn=ldbm database,cn=plugins,cn=config
# Create the index types (if required)
#  for attribute $index, backend database $db
########################################

# creates dn for indexing attribute $index
dn: cn=$index,cn=index,cn=$db,cn=ldbm database,cn=plugins,cn=config
changetype: add
objectclass: top
objectclass: nsIndex
cn: $index
nsSystemIndex: false
DN_ADD
      foreach $type (@indextypes) {
        print LDIF <<INDEX_ADD;

# index type $type
dn: cn=$index,cn=index,cn=$db,cn=ldbm database,cn=plugins,cn=config
changetype: modify
add: nsIndexType
nsIndexType: $type
INDEX_ADD
      }
    } else {
      # indexes for attr exist, check and see if all index types exist
      foreach $type (@indextypes) {
        if (index($currindex, $type) == -1) {
          log_msg "      index type $type does not exist\n";
          if ($needReindex == 0) {
            # first missing index type found
            $needReindex = 1;
            print LDIF <<DN_START;

########################################
# dn: cn=$index,cn=index,cn=$db,
#  cn=ldbm database,cn=plugins,cn=config
# Create the index types (if required)
#  for attribute $index, backend database $db
########################################
DN_START
          }
          print LDIF <<INDEX_ADD2;

# index type $type
dn: cn=$index,cn=index,cn=$db,cn=ldbm database,cn=plugins,cn=config
changetype: modify
add: nsIndexType
nsIndexType: $type
INDEX_ADD2
        }
      }
    }

    if ($needReindex == 1) {
      log_msg "      New indexes and Reindexing required\n";
    } else {
      log_msg "      No new indexes required\n";
    }

    if ($ReIndex =~ /^y|^Y/ && $needReindex == 1) {
      log_only "      ReIndex will happen for index type $index\n";
      # run the db2index.pl job for the index
      if ($DSversion >=5 && $DSversionMinor > 1) {
        $perlcmd = "$ServerRoot/bin/slapd/admin/bin/perl $ServerRoot/$DSinstance/db2index.pl  -D \"\$BINDDN\" \$pflag \"\$parg\" -n $db -t $index";
      } else {
        $perlcmd = "$ServerRoot/install/perl $ServerRoot/$DSinstance/db2index.pl -D \"\$BINDDN\" \$pflag \"\$parg\" -n $db -t $index";
      }
      print CMD "echo \"Running db2index.pl for index $index on $db\"\n";
      print CMD "$perlcmd\n";
      print CMD "stat=\$?\n";
      print CMD "echo Done status = \$stat \n";
      print CMD "if [ \$stat -ne 0 ]; then\n";
      print CMD "  echo \"WARNING: db2index.pl returned \$stat\"\n";
      print CMD "fi\n";
      # successive db2index needs a sleep because it generates a dn with a
      # timestamp with a resolution of one second.
      # This is because db2index needs to finish before you can issue the
      # next one.
      # I really should use the unpublished interface of looking at the 
      # generated dn by db2index.pl and read the task exit code.
      $num = @indextypes;
      $num = $num * 2;
      print CMD "echo Sleeping for $num seconds to allow indexing to finish...\n";
      print CMD "sleep $num\n";
    }
  }
  chdir $CurrDir;
  return;
}

########################################
# detect the schema type - 1 or 2
########################################
sub get_schema_type
{
  my($suffix) = @_;
  my($schematype);

  chdir $CmdDir;

  # check if schema 2 has been installed
  foreach $_ (`$SRCHCMD $LdapOpt -b "ou=DAI,ou=services,$suffix" -s one '(&(objectclass=top)(ou=*))' 2>&1`) {
    if ($_ =~ /No such object/i) {
      $schematype = $SCHEMA1_MODE;
      chdir $CurrDir;
      return 1;
      last;
    }
  }

  $schematype=$SCHEMA2NATIVE_MODE;
  chdir $CurrDir;

  return $schematype;
}

########################################
# Get the dssetup data stored in the directory
# Returns:
#  version, revision, dcsuffix, ugsuffix, schematype
# If the value does not exist, it returns the following:
#  version = 0
#  revision = 0
#  dcsuffix = ""
#  ugsuffix = ""
#  schematype = ""
########################################
sub GetStoredDssetupData
{
  my($version,$revision,$dcsuffix,$ugsuffix,$schematype);
  $version = 0;
  $revision = 0;
  $dcsuffix = "";
  $ugsuffix = "";
  $schematype = "";
  chdir $CmdDir;
  foreach $_ (`$SRCHCMD $LdapOpt -b "$CommServersDN, $InstallerSuffix" 'sunkeyvalue=*' 2>&1`) {
    if ($_ =~ /dssetup_ver/) {
      chomp;
      ($tmp,$version) = split(/=/, $_, 2);
      next;
    }
    if ($_ =~ /dssetup_rev/) {
      chomp;
      ($tmp,$revision) = split(/=/, $_, 2);
      next;
    }
    if ($_ =~ /dcsuffix/) {
      chomp;
      ($tmp,$dcsuffix) = split(/=/, $_, 2);
      next;
    }
    if ($_ =~ /ugsuffix/) {
      chomp;
      ($tmp,$ugsuffix) = split(/=/, $_, 2);
      next;
    }
    if ($_ =~ /schematype/) {
      chomp;
      ($tmp,$schematype) = split(/=/, $_, 2);
      next;
    }
  }
  chdir $CurrDir;
  debug_log "Stored ver=$version rev=$revision, dctree=$dcsuffix\n";
  debug_log "       ug=$ugsuffix, schematype=$schematype\n";
  return $version, $revision, $dcsuffix, $ugsuffix, $schematype;
}

########################################
# Add specific DN to record comm_dssetup information so that the installer
# can pick up the information.
########################################
sub create_installer_schema
{
  my($found);

  chdir $CmdDir;
  log_msg "  Generating ldif for Adding schema for installer metadata\n";
  print LDIF <<SCHEMA_MOD;

########################################
# dn: cn=schema
# Add the schema for sunkeyvalue. Required by sunCommsMetaData objectclass
# Note that sunkeyvalue may have already been added by Identity Server
########################################
dn: cn=schema
changetype: modify
add: attributeTypes
attributeTypes: ( 1.3.6.1.4.1.42.2.27.9.1.83 NAME ( 'sunkeyvalue' ) DESC 'Attribute to store the encoded key values of the services' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Sun ONE Identity Management' )

########################################
# dn: cn=schema
# Add the schema for the Comm Servers installer metadata information
########################################
dn: cn=schema
changetype: modify
add: objectClasses
objectClasses: ( 1.3.6.1.4.1.42.2.27.2.1.39 NAME 'sunCommsMetaData' DESC 'Objectclass for storing meta data for Sun Communication' SUP top STRUCTURAL MUST ( cn ) MAY ( sunkeyvalue ) )
SCHEMA_MOD

  create_naming_context($InstallerSuffix);

  chdir $CmdDir;

  # create the o=comms-config suffix
  #
  $found = 0;
  foreach $_ (`$SRCHCMD $LdapOpt -b "$InstallerSuffix" -s base objectclass=top 2>&1`) {
    if ($_ =~ /$InstallerSuffix/) {
      $found = 1;
      last;
    }
  }

  if (! $found ) {
    log_msg "  Generating ldif for Creating DN: $InstallerSuffix\n";
    print LDIF <<INST_SUFFIX

########################################
# dn: $InstallerSuffix
# Create the DN for the root suffix of metadata for Comms Products
########################################
dn: $InstallerSuffix
changetype: add
objectclass: top
objectclass: extensibleObject
INST_SUFFIX
  }

  $found = 1;
  foreach $_ (`$SRCHCMD $LdapOpt -b "$CommServersDN,$InstallerSuffix" -s base objectclass=top 2>&1`) {
    if ($_ =~ /No such object/i) {
      $found = 0;
      last;
    }
  }

  if (! $found ) {
    log_msg "  Generating ldif for Creating DN: $CommServersDN,$InstallerSuffix\n";
    if ($ConfigOnly) {
      print LDIF <<INSTALLER_ADDMOD_CONFIG;

########################################
# dn: $CommServersDN,$InstallerSuffix
# Create the DN for the installer metadata for Comm Servers
########################################
dn: $CommServersDN,$InstallerSuffix
changetype: add
objectclass: top
objectclass: sunCommsMetaData
sunkeyvalue: dssetup_ver=$DSsetupVer
sunkeyvalue: dssetup_rev=$DSsetupRev
INSTALLER_ADDMOD_CONFIG
    } else {
      print LDIF <<INSTALLER_ADDMOD;

########################################
# dn: $CommServersDN,$InstallerSuffix
# Create the DN for the installer metadata for Comm Servers
########################################
dn: $CommServersDN,$InstallerSuffix
changetype: add
objectclass: top
objectclass: sunCommsMetaData
sunkeyvalue: dssetup_ver=$DSsetupVer
sunkeyvalue: dssetup_rev=$DSsetupRev
sunkeyvalue: dcsuffix=$DCSuffix
sunkeyvalue: ugsuffix=$UGSuffix
sunkeyvalue: schematype=$SchemaType
INSTALLER_ADDMOD
    }
  } else {
    log_msg "  Generating ldif for updating DN for $CommServersDN,$InstallerSuffix\n";
    if ($ConfigOnly) {
      print LDIF <<INSTALLER_MOD_CONFIG;

########################################
# dn: $CommServersDN,$InstallerSuffix
# Update the installer metadata for Comm Servers
########################################
dn: $CommServersDN,$InstallerSuffix
changetype: modify
replace: sunkeyvalue
sunkeyvalue: dssetup_ver=$DSsetupVer
sunkeyvalue: dssetup_rev=$DSsetupRev
INSTALLER_MOD_CONFIG
    } else {
      print LDIF <<INSTALLER_MOD;

########################################
# dn: $CommServersDN,$InstallerSuffix
# Update the installer metadata for Comm Servers
########################################
dn: $CommServersDN,$InstallerSuffix
changetype: modify
replace: sunkeyvalue
sunkeyvalue: dssetup_ver=$DSsetupVer
sunkeyvalue: dssetup_rev=$DSsetupRev
sunkeyvalue: dcsuffix=$DCSuffix
sunkeyvalue: ugsuffix=$UGSuffix
sunkeyvalue: schematype=$SchemaType
INSTALLER_MOD
    }
  }

  chdir $CurrDir;
  return;
}

########################################
# Given a string that is a DN, strip the spaces around the = and ,
# Returns:
#  DN
########################################
sub convert2DN
{
  my($line) = @_;
  my(@fields);

  # strip spaces around =
  @fields = split(/=/, $line);
  foreach (@fields) {
    s/^\s*//;
    s/\s*$//;
# debugging
#    print "$_\n";
  }
  $line = join("=",@fields);
# debugging
#  print "\nline = $line \n";

  # strip spaces around ,
  @fields = split(/,/, $line);
  foreach (@fields) {
    s/^\s*//;
    s/\s*$//;
# debug
#    print "$_\n";
  }
  $line = join(",",@fields);
#  print "\nline = $line \n";
  return $line;
}

########################################
# Create preamble
# Uses CMD
########################################
sub create_preamble
{
  print CMD <<CMD_PREAMBLE;
#! /bin/sh
# This shell script contains the commands to run against the Directory
#set -x

#
# Usage
#
usage()
{
  echo "Usage: \$0 -D <binddn> [-w <passwd>] [-j <passwdfile>]"
  echo "  -D binddn        bind dn"
  echo "  -w <passwd>      bind passwd for simple ldap authentication"
  echo "  -j <passwdfile>  read bind passwd from 'file' for simple authentication"
  exit 1
}

if [ \$# -lt 1 ]
then
  echo "You must specify either -w or -j"
  usage
fi

jflag=0
wflag=0
Dflag=0
while getopts D:w:j: c; do
  case \$c in
  D)
    Dflag=1
    BINDDN=\$OPTARG;;
  w)
    wflag=1
    pflag=-w
    parg=\$OPTARG;;
  j)
    jflag=1
    pflag=-j
    parg=\$OPTARG;;
  \\?)
    usage;;
  esac
done
shift `expr \$OPTIND - 1`

if [ \$Dflag -eq 0 ]
then
  echo "You must specify the -D switch"
  usage
fi

if [ \$jflag -eq 0 -a \$wflag -eq 0 ]
then
  echo "You must specify either -w or -j"
  usage
fi

if [ \$jflag -eq 1 -a \$wflag -eq 1 ]
then
  echo "You can not specify both -w and -j. Please specify one or the other"
  usage
fi

if [ \$# -ge 1 ]
then
  echo "Unrecognized argument \$*"
  usage
fi

set `/usr/bin/id`
if [ \$1 != "uid=0(root)" ]; then
  echo
  echo "To run this utility you need to be the system's root user. \\n"
  echo 
  exit 1
fi
CMD_PREAMBLE
}
