#!/usr/bin/perl  -w
##
## Convert an LDIF file containing sambaAccount entries
## to the new sambaSamAccount objectclass
##
## Copyright Gerald (Jerry) Carter	2003
##
## Usage: convertSambaAccount --sid=<Domain SID> \
##       --input=<input ldif> --output=<output ldif> \
##       --changetype=[modify|add]
##
## You can generate an input ldif file using:
## $ ldapsearch -LL -x -h ldapsrv -D cn=root,dc=company,dc=com \
##   -b dc=copmany,dc=com > /tmp/samba3.alpha23.ldif
##
## Note the "-LL" so no additional comments are generated
##


use strict;
use Net::LDAP::LDIF;
use Getopt::Long;


##############################################################################
## local variables

my ( $domain, $domsid, $changetype );
my ( $ldif, $ldif2 );
my ( $entry, @objclasses, $obj );
my ( $is_samba_account, $is_samba_group );
my ( %attr_map, %group_attr_map, $key );
my ( @dels, $deletion, @adds, $addition );
my ( $result, %options );


##############################################################################
## Print the option usage

sub usage {

	print "convertSambaAccount <options>\n";
	print "Options:\n";
	print "  --help         print this help message\n";
	print "  --input        input LDIF filename\n";
	print "  --output       output LDIF filename\n";
	print "  --sid          domain SID\n";
	print "  --changetype   [modify|add] (default is 'add')\n";
}


##############################################################################
##                               MAIN DRIVER                                ##
##############################################################################

##
## hashes to map old attribute names to new ones 
##

%attr_map = ( 
	lmPassword	=> 'sambaLMPassword',
	ntPassword	=> 'sambaNTPassword',
	pwdLastSet	=> 'sambaPwdLastSet',
	pwdMustChange	=> 'sambaPwdMustChange',
	pwdCanChange	=> 'sambaPwdCanChange',
	homeDrive	=> 'sambaHomeDrive',
	smbHome		=> 'sambaHomePath',
	scriptPath	=> 'sambaLogonScript',
	profilePath	=> 'sambaProfilePath',
	kickoffTime	=> 'sambaKickoffTime',
	logonTime	=> 'sambaLogonTime',
	logoffTime	=> 'sambaLogoffTime',
	userWorkstations	=> 'sambaUserWorkstations',
	domain		=> 'sambaDomainName',
	acctFlags	=> 'sambaAcctFlags',
);

%group_attr_map = (
	ntSid		=> 'sambaSID',
	ntGroupType	=> 'sambaGroupType',
);

##
## process command line args
##

$result = GetOptions(\%options,
			"help", 
			"input=s", 
			"output=s", 
			"sid=s",
			"changetype=s");

if (!$result && ($#ARGV != -1)) {
	usage();
	exit 1;
}

if ( defined($options{'help'}) ) {
	usage();
	exit 0;
}


if ( !defined( $options{'sid'} ) ) {
	print "You must provide a domain sid\n";
	exit 1;
}

$domsid = $options{'sid'};

$changetype = 'add';
if ( defined( $options{'changetype'} ) ) {
	$changetype = $options{'changetype'};
}

##
## open files
##

$ldif = Net::LDAP::LDIF->new ($options{'input'}, "r") or die $!;

if ( "$changetype" eq "add" ) {
	$ldif2 = Net::LDAP::LDIF->new ($options{'output'}, "w") or die $!;
}
elsif ( "$changetype" eq "modify" ) {
	open( OUTPUT, ">$options{'output'}" ) or die $!;
}
else {
	print "Bad changetype!\n";
	exit 1;
}

##
## process LDIF 
##

while ( !$ldif->eof ) {
	undef ( $entry );
	$entry = $ldif->read_entry();

	## skip entry if we find an error
	if ( $ldif->error() ) {
		print "Error msg: ",$ldif->error(),"\n";
		print "Error lines:\n",$ldif->error_lines(),"\n";
		next;
	}

	##
	## check to see if we have anything to do on this
	## entry.  If not just write it out
	##
	@objclasses = $entry->get_value( "objectClass" );
	undef ( $is_samba_account );
	undef ( $is_samba_group );
	@adds = ();
	@dels = ();
	foreach $obj ( @objclasses ) {
		if ( lc($obj) eq "sambaaccount" ) {
			$is_samba_account = 1;
		} elsif ( lc($obj) eq "sambagroupmapping" ) {
			$is_samba_group = 1;
		}
	}

	if ( defined ( $is_samba_account ) ) {
		##
		## start editing the sambaAccount
		##

		@dels = ( 'objectclass: sambaAccount', 'rid' );
		@adds = ('objectclass: sambaSamAccount', "sambaSID: " .  ${domsid} . "-" . ${entry}->get_value( 'rid' ) );
		$entry->delete( 'objectclass' => [ 'sambaAccount' ] );
		$entry->add( 'objectclass' => 'sambaSamAccount' );

		$entry->add( 'sambaSID' => $domsid."-".$entry->get_value( "rid" ) );
		$entry->delete( 'rid' );
	
		if ( defined($entry->get_value( "primaryGroupID" )) ) {
			push @adds, "sambaPrimaryGroupSID: " . $domsid."-".$entry->get_value( "primaryGroupID" );
			push @dels, "primaryGroupID";
			$entry->add( 'sambaPrimaryGroupSID' => $domsid."-".$entry->get_value( "primaryGroupID" ) );
			$entry->delete( 'primaryGroupID' );
		}
	

		foreach $key ( keys %attr_map ) {
			if ( defined($entry->get_value($key)) ) {
				push @adds, "$attr_map{$key}: " . $entry->get_value($key);
				push @dels, "$key";
				$entry->add( $attr_map{$key} => $entry->get_value($key) );
				$entry->delete( $key );
			}
		}
	} elsif ( defined ( $is_samba_group ) ) {
		foreach $key ( keys %group_attr_map ) {
			if ( defined($entry->get_value($key)) ) {
				push @adds, "$group_attr_map{$key}: " . $entry->get_value($key);
				push @dels, "$key";
				$entry->add( $group_attr_map{$key} => $entry->get_value($key) );
				$entry->delete( $key );
			}
		}
	}
	
	## see if we should write full entries or only the changes
	
	if ( "$changetype" eq "add" ) {
		$ldif2->write_entry( $entry );
	}
	else {
		if ( defined ( $is_samba_account ) || defined ( $is_samba_group ) ){
			if ( @adds + @dels > 0 ) {
				print OUTPUT "dn: " . $entry->dn . "\n";
				foreach $addition (@adds) {
					$addition =~ /(^\w+):/;
					print OUTPUT "add: " . $1  . "\n";
					print OUTPUT "$addition\n-\n";
				}
				foreach $deletion (@dels) {
					if ( $deletion =~ /^(\w+):\s(.*)/ ) {
						print OUTPUT "delete: $1\n$1: $2\n-\n";
					} else {
						print OUTPUT "delete: $deletion\n-\n"
					}
				}
				print OUTPUT "\n"
			}
		}
	}
}