#!/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) 2001 Sun Microsystems, Inc. Used by permission.
# Copyright (C) 2005 Red Hat, Inc.
# All rights reserved.
# END COPYRIGHT BLOCK
#

# Migrate a SuiteSpot 2.X or 3.X localdb to a 4.0 directory server

BEGIN {
	$isNT = -d '\\';
	$PATHSEP = $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 ? "\"" : "";
}

sub getNextEntry {
	my $fh = shift;
	my @entry = (); # an array of strings, each string is 1 attr/value pair
	my $line = "";
	while (($line = <$fh>) && !($line =~ /^$/)) { # entry is terminated by EOF or empty line34
		chop $line;
		if ($line =~ /^\s/) { # line begins with a single space char
			$entry[@entry-1] .= $'; # add continuation to line
		} else {
			push @entry, $line;
		}
	}
	return @entry;
}

# given a string of the form string:value, return everything to the left of the :
sub getAttrName {
	my $s = shift;
	$s =~ s/[:].*$//;
	return $s;
}

sub printEntry {
	my $fh = shift;
	foreach (@_) {
		print $fh $_, "\n";
	}
	print $fh "\n";
}

sub usage {
	print 'Usage: perl migrateLocalDb <userdb> <new suffix> [<new instance>]', "\n";
	print "\t", '<userdb> - full path to the userdb directory to migrate', "\n";
	print "\t", '    e.g. /usr/netscape/suitespot3/userdb', "\n";
	print "\t", '<new suffix> - new suffix e.g. dc=example,dc=com; may be empty', "\n";
	print "\t", '<new instance> - full path to the destination instance', "\n";
	print "\t", '    e.g. /usr/netscape/server4/slapd-foo', "\n";
	print "The new instance is optional.  If not given, the local db will\n";
	print "be converted to the LDIF file userdb/localdb.ldif, but\n";
	print "it will not be added to the database of the new instance.\n";
}

sub sigDieHandler {
	print @_, "\n";
	print "\n";
	&usage();
	print "\n";
	print "NMC_STATUS: ", 0+$!, "\n";
	exit $!;
}

$SIG{__DIE__} = 'sigDieHandler';

# check for command line arguments
if (@ARGV > 0) {
	$localDBPath = $ARGV[0];
	$newSuffix = $ARGV[1];
	$instanceDir = $ARGV[2];
	$bindDN = $ARGV[3];
	$bindPwd = $ARGV[4];
	# the perl executable should be in server root/install/
	$relPath = '/install/';
	$relPath =~ s#/#\\#g if ($isNT);
	($sroot = $) =~ s#$relPath.*$##;
} elsif ($ENV{'REQUEST_METHOD'}) {
	$| = 1;
	# print CGI header
	print "Content-type: text/plain\n\n";

	# process the CGI input
	use Cgi;

	# get the server root directory
	$sroot = $ENV{'NETSITE_ROOT'};

	$localDBPath = $cgiVars{'localDBPath'};
	$newSuffix = $cgiVars{'newSuffix'};
	$instanceDir = $cgiVars{'instanceDir'};
	$bindDN = $cgiVars{'bindDN'};
	$bindPwd = $cgiVars{'bindPwd'};
} else {
	die "";
}

# this is a table of attributes which have DN syntax
%dnAttrs = (
	'aliasedobjectname', "\n",
	'member', "\n",
	'owner', "\n",
	'roleoccupant', "\n",
	'seealso', "\n",
	'dn', "\n",
	'uniquemember', "\n",
	'creatorsname', "\n",
	'modifiersname', "\n",
	'manager', "\n",
	'documentauthor', "\n",
	'secretary', "\n",
	'associatedname', "\n",
	'ditredirect', "\n",
	'targetdn', "\n",
	'newrdn', "\n",
	'newsuperior', "\n",
	'lastmodifiedby', "\n",
	'replicaroot', "\n",
	'replicabinddn', "\n",
	'cirreplicaroot', "\n",
	'cirbinddn', "\n",
	'vlvbase', "\n",
	'netscapemdsuffix', "\n",
	'changelog', "\n",
	'obsoletedbydocument', "\n",
	'obsoletesdocument', "\n",
	'reciprocalnaminglink', "\n",
	'updatedbydocument', "\n",
	'updatesdocument', "\n"
);

print "Begin local db migration\n";

# see if the parameters are valid
# check localdb path
die "Error: could not find the local db $localDBPath" if (! -d $localDBPath);
# check suffix?

# get the old server root directory
# step 1: convert the local db to an ldif file
# lookup the old suffix from the lcache.conf
$lcache = $localDBPath . $PATHSEP . 'ldap' . $PATHSEP . 'config' . $PATHSEP .
	'lcache.conf';
open(LCACHE, "$lcache") or die "Error: could not open config file $lcache";
while (<LCACHE>) {
	chop;
	if (/^suffix\s+/i) {
		$oldSuffix = $';
		$oldSuffix =~ s/^[\"]//;
		# trim leading "
		$oldSuffix =~ s/[\"]$//;
		# trim trailing "
		print "The old suffix is $oldSuffix\n";
	}
}
close(LCACHE);

print "Converting the local db to LDIF . . .\n";
# run the ldapsearch -C command
$cmddir = $localDBPath . $PATHSEP . 'ldap' . $PATHSEP . 'tools';
@cmd = ($quote . $cmddir . $PATHSEP . 'ldapsearch' . $quote, '-C',
		"${quote}$lcache${quote}",
		'-s', 'sub', '-b', "\"$oldSuffix\"", '"objectclass=*"');
chdir($cmddir) or die "Error: could not change to directory $cmddir";
open(READCMD, "${quote}@cmd${quote}|") or die "Error: could not execute @cmd";
if ($instanceDir) {
	$outputFile = $instanceDir . $PATHSEP . 'ldif' . $PATHSEP . 'localdb.ldif';
} else {
	$outputFile = $localDBPath . $PATHSEP . 'localdb.ldif';
}

open(OUT, ">$outputFile") or die "Error: could not write file $outputFile";
while (@entry = getNextEntry(\*READCMD)) {
	# for each entry, replace the old suffix with the new one; if there
	# was no old suffix, just append the new one to the DN value attrs
	if ($newSuffix && $newSuffix ne '""') {
		if ($oldSuffix && $oldSuffix ne '""') {
			grep { s/$oldSuffix/$newSuffix/ig } @entry;
		} else {
			for ($ii = 0; $ii < @entry; ++$ii) {
				$name = &getAttrName($entry[$ii]);
				if ($dnAttrs{lc($name)}) {
					$entry[$ii] .= ", $newSuffix";
				}
			}
		}
	}

	printEntry(\*OUT, @entry);
}
close(READCMD);
close(OUT);

if ($? != 0) {
	die "Error: could not read local db from $localDBPath";
} elsif (! -s $outputFile) {
	die "Error: converted local db is empty";
}

# check instance dir

if ($instanceDir) {
	if (! -d $instanceDir) {
		# use may have given relative path
		$instanceDir = $sroot . $PATHSEP . $instanceDir;
		die "Error: could not find the instance dir $instanceDir in server root $sroot"
			if (! -d $instanceDir);
	}

# step 2: load the converted LDIF file into the target directory server
# if the bindDN and password were given, attempt to use ldif2ldap, otherwise,
# shutdown the server and use ldif2db

	if ($bindDN && $bindPwd) {
	} else {
		print "Shutting down the server . . .\n";
		# shutdown the server
		$stopCmd = $quote . $instanceDir . $PATHSEP . 'stop-slapd' . $script_suffix . $quote;
		system($stopCmd);
		print "Warning: could not shutdown the server in $instanceDir.\nThe server may already be down." if ($? != 0);
		sleep(10); # give the server time to shutdown

		# add the new suffix to the slapd.ldbm.conf
		if ($newSuffix && $newSuffix ne '""') {
			print "Adding suffix $newSuffix . . .\n";
			$slc = $instanceDir . $PATHSEP . 'config' . $PATHSEP . 'slapd.ldbm.conf';
			open(SLC, ">>$slc") or
				print "Warning: could not add the suffix $newSuffix: import may fail.\n";
			print SLC "suffix\t\"$newSuffix\"\n";
			close(SLC);
		}

		print "Importing the local db LDIF file . . .\n";
		# import the LDIF file
		@impCmd = ($quote . $instanceDir . $PATHSEP . 'ldif2db' . $quote,
				   '-C', '-i', "${quote}$outputFile${quote}");
		system(@impCmd);
		die "Error: could not import LDIF file $outputFile" if ($? != 0);

		print "Restarting the server . . .\n";
		# start the server
		$startCmd = $quote . $instanceDir . $PATHSEP . 'start-slapd' . $script_suffix . $quote;
		system($startCmd);
		print "Warning: could not restart the server in $instanceDir" if ($? != 0);
	}

	print "Finished.  The local db has been imported to $instanceDir.\n";
} else {
	print "Finished.  The local db has been written to $outputFile.\n";
}

if (%cgiVars) {
	print "NMC_STATUS: 0\n";
}

exit 0;
