420 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			420 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
#!/usr/bin/perl -T
 | 
						|
 | 
						|
use strict;
 | 
						|
use warnings;
 | 
						|
use Net::LDAP;
 | 
						|
use Net::LDAP::LDIF;
 | 
						|
use Date::Parse;
 | 
						|
use esmith::ConfigDB;
 | 
						|
use esmith::AccountsDB;
 | 
						|
use esmith::util;
 | 
						|
use Getopt::Long qw(:config bundling);
 | 
						|
 | 
						|
$ENV{'PATH'} = '/bin:/usr/bin:/sbin:/usr/sbin';
 | 
						|
$ENV{'LANG'} = 'C';
 | 
						|
$ENV{'TZ'} = '';
 | 
						|
 | 
						|
sub dnsort {
 | 
						|
    my %type = ( add => 1, modrdn => 2, moddn => 2, modify => 3, delete => 4);
 | 
						|
    my %attr = ( dc => 1, ou => 2, cn => 3, uid => 4);
 | 
						|
 | 
						|
    my ($oa) = ($a->get_value('newrdn') || $a->dn) =~ /^([^=]+)=/;
 | 
						|
    my ($ob) = ($b->get_value('newrdn') || $b->dn) =~ /^([^=]+)=/;
 | 
						|
    my ($ua, $ub) = map { my $tu = $_->get_value('uidnumber'); defined $tu && $tu ne '' ? $tu : -1 } ($a, $b);
 | 
						|
    my ($ga, $gb) = map { my $tg = $_->get_value('gidnumber'); defined $tg && $tg ne '' ? $tg : -1 } ($a, $b);
 | 
						|
 | 
						|
    ($attr{$oa} || 9) <=> ($attr{$ob} || 9) || ($type{$a->changetype} || 9) <=> ($type{$b->changetype} || 9) || 
 | 
						|
    $ua <=> $ub || $ga <=> $gb || ($a->get_value('newrdn') || $a->dn) cmp ($b->get_value('newrdn') || $b->dn);
 | 
						|
}
 | 
						|
 | 
						|
my $c = esmith::ConfigDB->open_ro;
 | 
						|
my $a = esmith::AccountsDB->open_ro;
 | 
						|
 | 
						|
my $auth = $c->get('ldap')->prop('Authentication') || 'disabled';
 | 
						|
my $schema = '/etc/openldap/schema/samba.schema';
 | 
						|
 | 
						|
my $domain = $c->get('DomainName')->value;
 | 
						|
my $basedn = esmith::util::ldapBase($domain);
 | 
						|
 | 
						|
my $userou = 'ou=Users';
 | 
						|
my $groupou = 'ou=Groups';
 | 
						|
my $compou = 'ou=Computers';
 | 
						|
 | 
						|
my ($dc) = split /\./, $domain;
 | 
						|
my $company = $c->get_prop('ldap', 'defaultCompany') || $domain;
 | 
						|
 | 
						|
my %opt;
 | 
						|
GetOptions ( \%opt, "diff|d", "update|u", "input|i=s", "output|o=s" );
 | 
						|
$opt{input} = '/usr/sbin/slapcat -c 2> /dev/null|' unless $opt{input} && ($opt{input} eq '-' || -f "$opt{input}" || -c "$opt{input}");
 | 
						|
$opt{diff} = 1 if $opt{update};
 | 
						|
if ( $opt{output} && $opt{output} =~ m{^([-\w/.]+)$}) {
 | 
						|
    $opt{output} = $1;
 | 
						|
} else {
 | 
						|
    $opt{output} = '-';
 | 
						|
}
 | 
						|
 | 
						|
my ($data, $dn);
 | 
						|
 | 
						|
# Top object (base)
 | 
						|
$data->{$basedn} = {
 | 
						|
    objectclass => [qw/organization dcObject top/],
 | 
						|
    dc          => $dc,
 | 
						|
    o           => $company,
 | 
						|
};
 | 
						|
 | 
						|
# Top containers for users/groups/computers
 | 
						|
foreach (qw/Users Groups Computers/) {
 | 
						|
    $data->{"ou=$_,$basedn"} = {
 | 
						|
        objectclass => [qw/organizationalUnit top/],
 | 
						|
        ou          => $_,
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
# Common accounts needed for SME to work properly
 | 
						|
$data->{"cn=nobody,$groupou,$basedn"}->{objectclass} = [ qw/posixGroup/ ];
 | 
						|
$data->{"uid=www,$userou,$basedn"}->{objectclass} = [ qw/account/ ];
 | 
						|
$data->{"cn=www,$groupou,$basedn"} = { objectclass => [ qw/posixGroup/ ], memberuid   => [ qw/admin/ ] };
 | 
						|
$data->{"cn=rsshusers,$groupou,$basedn"}->{objectclass} = [ qw/posixGroup/ ];
 | 
						|
$data->{"cn=shared,$groupou,$basedn"} = {
 | 
						|
    objectclass => [ qw/posixGroup mailboxRelatedObject/ ],
 | 
						|
    mail        => "everyone\@$domain",
 | 
						|
    memberuid   => [ qw/www/ ]
 | 
						|
};
 | 
						|
 | 
						|
# Read in accounts database information
 | 
						|
foreach my $acct ($a->get('admin'), $a->users, $a->groups, $a->ibays, $a->get_all_by_prop(type => 'machine')) {
 | 
						|
    my $key = $acct->key;
 | 
						|
    my $type = $acct->prop('type');
 | 
						|
 | 
						|
    next if $key eq 'Primary';
 | 
						|
 | 
						|
    $dn = "uid=$key,".($type eq 'machine' ? $compou : $userou).",$basedn";
 | 
						|
    if ($type =~ /^(?:user|group|machine|ibay)$/ || $key eq 'admin') {
 | 
						|
        if ($type eq 'user' || $key eq 'admin') {
 | 
						|
            # Allow removal of obsolete person objectclass and samba attributes
 | 
						|
            push @{$data->{$dn}->{_delete}->{objectclass}}, 'person';
 | 
						|
            
 | 
						|
 | 
						|
            push @{$data->{$dn}->{objectclass}}, 'inetOrgPerson';
 | 
						|
            $data->{$dn}->{mail} = "$key\@$domain";
 | 
						|
            @{$data->{$dn}}{qw/givenname sn telephonenumber o ou l street/} =
 | 
						|
                map { $acct->prop($_) || [] } qw/FirstName LastName Phone Company Dept City Street/;
 | 
						|
            $data->{$dn}->{cn} = $acct->prop('FirstName').' '.$acct->prop('LastName');
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            push @{$data->{$dn}->{objectclass}}, 'account';
 | 
						|
        }
 | 
						|
 | 
						|
        # users/ibays need to be a member of shared
 | 
						|
        push @{$data->{"cn=shared,$groupou,$basedn"}->{memberuid}}, $key if $type =~ /^(user|ibay)$/ || $key eq 'admin';
 | 
						|
 | 
						|
        # users need to be a member of rsshusers if their shell is /usr/bin/rssh
 | 
						|
        push @{$data->{"cn=rsshusers,$groupou,$basedn"}->{memberuid}}, $key if ($type =~ /^(user)$/ || $key eq 'admin') && (($acct->prop('Shell') || '/usr/bin/rssh') eq '/usr/bin/rssh');
 | 
						|
 | 
						|
        if ($auth ne 'enabled') {
 | 
						|
            # Allow removal of shadow properties
 | 
						|
            push @{$data->{$dn}->{_delete}->{objectclass}}, 'shadowAccount';
 | 
						|
            $data->{$dn}->{_delete}->{lc($_)} = 1 foreach qw/userPassword shadowLastChange shadowMin shadowMax
 | 
						|
                                                             shadowWarning shadowInactive shadowExpire shadowFlag/;
 | 
						|
 | 
						|
            if ( -f "$schema" ) {
 | 
						|
                # If we will be adding samba properties then allow removal
 | 
						|
                push @{$data->{$dn}->{_delete}->{objectclass}}, 'sambaSamAccount';
 | 
						|
                $data->{$dn}->{_delete}->{lc($_)} = 1 foreach qw/displayName sambaAcctFlags sambaLMPassword sambaNTPassword 
 | 
						|
                                                                 sambaNTPassword sambaPrimaryGroupSID sambaPwdLastSet sambaSID/;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    $dn = "cn=$key,$groupou,$basedn";
 | 
						|
    push @{$data->{$dn}->{objectclass}}, 'posixGroup';
 | 
						|
    if ($type eq 'group') {
 | 
						|
        # Allways replace memberuid with new set
 | 
						|
        $data->{$dn}->{_delete}->{memberuid} = 1;
 | 
						|
 | 
						|
        push @{$data->{$dn}->{objectclass}}, 'mailboxRelatedObject';
 | 
						|
 | 
						|
        $data->{$dn}->{mail} = "$key\@$domain";
 | 
						|
        $data->{$dn}->{description} = $acct->prop('Description') || [];
 | 
						|
        push @{$data->{$dn}->{memberuid}}, split /,/, ($acct->prop('Members') || '');
 | 
						|
 | 
						|
        # www needs to be a memeber of every group
 | 
						|
        push @{$data->{$dn}->{memberuid}}, 'www';
 | 
						|
 | 
						|
        if ($auth ne 'enabled' && -f "$schema" ) {
 | 
						|
            # If we will be adding samba properties then allow removal
 | 
						|
            push @{$data->{$dn}->{_delete}->{objectclass}}, 'sambaGroupMapping';
 | 
						|
            $data->{$dn}->{_delete}->{lc($_)} = 1 foreach qw/displayName sambaGroupType sambaSID/;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    elsif ($type eq 'ibay') {
 | 
						|
        $dn = "cn=".$acct->prop('Group').",$groupou,$basedn";
 | 
						|
        push @{$data->{$dn}->{memberuid}}, $acct->key;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
if ($auth ne 'enabled') {
 | 
						|
    # Read in information from unix (passwd) system 
 | 
						|
    open PASSWD, '/etc/passwd';
 | 
						|
    while (<PASSWD>) {
 | 
						|
        chomp;
 | 
						|
        my @passwd = split /:/, $_;
 | 
						|
        next unless scalar @passwd == 7;
 | 
						|
 | 
						|
        $dn = "uid=$passwd[0],".($passwd[0] =~ /\$$/ ? $compou : $userou).",$basedn";
 | 
						|
        next unless exists $data->{$dn};
 | 
						|
 | 
						|
        push @{$data->{$dn}->{objectclass}}, 'posixAccount';
 | 
						|
        @{$data->{$dn}}{qw/cn uid uidnumber gidnumber homedirectory loginshell/} =
 | 
						|
            map { $passwd[$_] ? $passwd[$_] : [] } (4,0,2,3,5,6);
 | 
						|
    }
 | 
						|
    close (PASSWD);
 | 
						|
 | 
						|
    # Shadow file defaults (pulled from cpu.conf)
 | 
						|
    my %shadow_def = ( 1 => [], 2 => 11192, 3 => -1, 4 => 99999, 5 => 7, 6 => -1, 7 => -1, 8 => 134538308 );
 | 
						|
 | 
						|
    # Read in information from unix (shadow) system 
 | 
						|
    open SHADOW, '/etc/shadow';
 | 
						|
    while (<SHADOW>) {
 | 
						|
        chomp;
 | 
						|
        my @shadow = split /:/, $_;
 | 
						|
        next unless scalar @shadow >= 6;
 | 
						|
        $shadow[1] = '!*' if $shadow[1] eq '!!';
 | 
						|
        $shadow[1] = "{CRYPT}$shadow[1]" unless $shadow[1] =~ /^\{/;
 | 
						|
 | 
						|
        $dn = "uid=$shadow[0],".($shadow[0] =~ /\$$/ ? $compou : $userou).",$basedn";
 | 
						|
        next unless exists $data->{$dn};
 | 
						|
 | 
						|
        push @{$data->{$dn}->{objectclass}}, 'shadowAccount';
 | 
						|
        @{$data->{$dn}}{ map { lc($_) } qw/userPassword shadowLastChange shadowMin shadowMax shadowWarning shadowInactive 
 | 
						|
                                           shadowExpire shadowFlag/} = map { $shadow[$_] ? $shadow[$_] : $shadow_def{$_} } (1..8);
 | 
						|
    }
 | 
						|
    close (SHADOW);
 | 
						|
 | 
						|
    # Read in information from unix (group) system 
 | 
						|
    open GROUP, '/etc/group';
 | 
						|
    while (<GROUP>) {
 | 
						|
        chomp;
 | 
						|
        my @group = split /:/, $_;
 | 
						|
        next unless scalar @group >= 3;
 | 
						|
        $group[3] = [ split /,/, ($group[3] || '') ];
 | 
						|
 | 
						|
        $dn = "cn=$group[0],$groupou,$basedn";
 | 
						|
        next unless exists $data->{$dn};
 | 
						|
 | 
						|
        push @{$data->{$dn}->{objectclass}}, 'posixGroup';
 | 
						|
        @{$data->{$dn}}{qw/cn gidnumber/} = map { $group[$_] ? $group[$_] : [] } (0,2);
 | 
						|
        push @{$data->{$dn}->{memberuid}}, @{$group[3]};
 | 
						|
    }
 | 
						|
    close (GROUP);
 | 
						|
 | 
						|
    my %smbprop = (
 | 
						|
        'User SID'            => 'sambasid',
 | 
						|
        'Account Flags'       => 'sambaacctflags',
 | 
						|
        'Primary Group SID'   => 'sambaprimarygroupsid',
 | 
						|
        'Full Name'           => 'displayname',
 | 
						|
        'Password last set'   => 'sambapwdlastset',
 | 
						|
    );
 | 
						|
 | 
						|
    # Read in information from unix (smbpasswd) system 
 | 
						|
    if ( -f "$schema" && -x '/usr/bin/pdbedit' ) {
 | 
						|
        $dn = undef;
 | 
						|
        open SMBDETAIL, '/usr/bin/pdbedit -vL 2> /dev/null|';
 | 
						|
        while (<SMBDETAIL>) {
 | 
						|
            chomp;
 | 
						|
 | 
						|
            $dn = ("uid=$1,".($1 =~ /\$$/ ? $compou : $userou).",$basedn") if m/^Unix username:\s+(\S.*)$/;
 | 
						|
            next unless $dn && exists $data->{$dn};
 | 
						|
 | 
						|
            # Map the samba account properties that we care about
 | 
						|
            $data->{$dn}->{$smbprop{$1}} = ($2 ? str2time($2) : (defined $3 ? $3 : []))
 | 
						|
                if m/^(.+):\s+(?:(\S.*\d{4} \d{2}:\d{2}:\d{2}.*)|(.*))$/ && exists $smbprop{$1};
 | 
						|
        }
 | 
						|
        close (SMBDETAIL);
 | 
						|
 | 
						|
        open SMBPASSWD, '/usr/bin/pdbedit -wL 2> /dev/null|';
 | 
						|
        while (<SMBPASSWD>) {
 | 
						|
            chomp;
 | 
						|
            my @smbpasswd = split /:/, $_;
 | 
						|
            next unless scalar @smbpasswd >= 6;
 | 
						|
 | 
						|
            $dn = "uid=$smbpasswd[0],".($smbpasswd[0] =~ /\$$/ ? $compou : $userou).",$basedn";
 | 
						|
            next unless exists $data->{$dn} && exists $data->{$dn}->{uidnumber} && $data->{$dn}->{uidnumber} eq $smbpasswd[1];
 | 
						|
 | 
						|
            push @{$data->{$dn}->{objectclass}}, 'sambaSamAccount';
 | 
						|
            @{$data->{$dn}}{qw/sambalmpassword sambantpassword/} = map { $smbpasswd[$_] ? $smbpasswd[$_] : [] } (2,3);
 | 
						|
        }
 | 
						|
        close (SMBPASSWD);
 | 
						|
    }
 | 
						|
 | 
						|
    if ( -f "$schema" && -x '/usr/bin/net' ) {
 | 
						|
        open GROUPMAP, '/usr/bin/net groupmap list 2> /dev/null|';
 | 
						|
        while (<GROUPMAP>) {
 | 
						|
            chomp;
 | 
						|
 | 
						|
            if (m/^(.+) \((.+)\) -> (.+)$/) {
 | 
						|
                # Skip local machine accounts
 | 
						|
                next if $2 =~ /S-1-5-32-\d+/;
 | 
						|
 | 
						|
                $dn = "cn=$3,$groupou,$basedn";
 | 
						|
                next unless exists $data->{$dn};
 | 
						|
 | 
						|
                push @{$data->{$dn}->{objectclass}}, 'sambaGroupMapping';
 | 
						|
                @{$data->{$dn}}{qw/displayname sambasid sambagrouptype/} = ($1, $2, 2);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        close (GROUPMAP);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
my @ldif;
 | 
						|
 | 
						|
# Loop through ldap data and update as necessary
 | 
						|
my $reader = Net::LDAP::LDIF->new( $opt{input}, 'r', onerror => 'undef' );
 | 
						|
while( not $reader->eof()) {
 | 
						|
    my $entry = $reader->read_entry() || next;
 | 
						|
    $dn = $entry->dn;
 | 
						|
 | 
						|
    # Ensure the basedn is correct
 | 
						|
    $dn = "$1$basedn" if $dn =~ /^((?:(?!dc=)[^,]+,)*)dc=/;
 | 
						|
 | 
						|
    # Ensure correct ou is part of user/groups/computers
 | 
						|
    if ($dn =~ /^(uid=([^,\$]+)(\$)?),((?:(?!dc=)[^,]+,)*)dc=/) {
 | 
						|
        if ( defined $3 && $3 eq '$') {
 | 
						|
            $dn = "$1,$compou,$basedn";
 | 
						|
        }
 | 
						|
        elsif (grep /posixGroup/, @{$entry->get_value('objectclass', asref => 1) || []}) {
 | 
						|
            $dn = "cn=$2,$groupou,$basedn";
 | 
						|
            
 | 
						|
            # Cleanup attributes that the modrdn will perform
 | 
						|
            $entry->add(cn => $2);
 | 
						|
            $entry->delete(uid => [$2]);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            $dn = "$1,$userou,$basedn";
 | 
						|
        }
 | 
						|
    }
 | 
						|
    elsif ($dn =~ /^(cn=[^,]+),((?:(?!dc=)[^,]+,)*)dc=/) {
 | 
						|
        $dn = "$1,$groupou,$basedn" unless $2 =~ /^ou=auto\./;
 | 
						|
    }
 | 
						|
 | 
						|
    # Don't process records twice
 | 
						|
    next if $data->{$dn}->{_done};
 | 
						|
 | 
						|
    # Rename existing entry into place if we can
 | 
						|
    if ($dn ne $entry->dn) {
 | 
						|
        my $rdn = Net::LDAP::Entry->new;
 | 
						|
        $rdn->dn($entry->dn);
 | 
						|
        $rdn->changetype('modrdn');
 | 
						|
        my ($newdn, $newbase) = split /,/, $dn, 2;
 | 
						|
        $rdn->add(newrdn => $newdn, deleteoldrdn => 1, newsuperior => $newbase);
 | 
						|
        push @ldif, $rdn;
 | 
						|
 | 
						|
        # Now we can change the entry to new dn
 | 
						|
        $entry->dn($dn);
 | 
						|
    }
 | 
						|
 | 
						|
    # Change type to modify so that we can keep track of changes we make
 | 
						|
    $entry->changetype('modify');
 | 
						|
 | 
						|
    # Hack to make upgrades work (add calEntry if calFGUrl attributes exists)
 | 
						|
    if ($entry->exists('calFBURL') && -f "/etc/openldap/schema/rfc2739.schema") {
 | 
						|
        push @{$data->{$dn}->{objectclass}}, 'calEntry';
 | 
						|
    }
 | 
						|
 | 
						|
    my %attributes = ();
 | 
						|
    @attributes{ keys %{$data->{$dn}}, exists $data->{$dn}->{_delete} ? map { lc($_) } keys %{$data->{$dn}->{_delete}} : () } = ();
 | 
						|
 | 
						|
    foreach my $attr (sort keys %attributes) {
 | 
						|
        # Skip the pseudo attributes
 | 
						|
        next if $attr =~ /^_/;
 | 
						|
 | 
						|
        my @l = @{$entry->get_value($attr, asref => 1) || []};
 | 
						|
        my @u = exists $data->{$dn}->{$attr} ? (ref $data->{$dn}->{$attr} ? @{$data->{$dn}->{$attr}} : ($data->{$dn}->{$attr})) : ();
 | 
						|
 | 
						|
        # Figure out differences between attributes
 | 
						|
        my (@lonly, @uonly, @donly, %lseen, %useen, %dseen) = () x 6;
 | 
						|
 | 
						|
        # Unique lists of what is in ldap and what needs to be in ldap
 | 
						|
        @lseen{@l} = ();
 | 
						|
        @useen{@u} = ();
 | 
						|
 | 
						|
        # Create list of attributes that aren't in the other
 | 
						|
        @uonly = grep { ! exists $lseen{$_} } keys %useen;
 | 
						|
        @lonly = grep { ! exists $useen{$_} } keys %lseen;
 | 
						|
 | 
						|
        # Determine which of the ldap only attributes we need to remove
 | 
						|
        if ((keys %useen == 1 && keys %lseen == 1) || (keys %useen == 0 && exists $data->{$dn}->{$attr})) {
 | 
						|
            # Replacing a single entry or erasing entire entry
 | 
						|
            @donly = @lonly;
 | 
						|
        }
 | 
						|
        elsif ($data->{$dn}->{_delete} && $data->{$dn}->{_delete}->{$attr}) {
 | 
						|
            if (my $ref = ref($data->{$dn}->{_delete}->{$attr})) {
 | 
						|
                # Map hash keys or array elemts to valid values to delete
 | 
						|
                @dseen{$ref eq 'HASH' ? keys %{$data->{$dn}->{_delete}->{$attr}} : @{$data->{$dn}->{_delete}->{$attr}}} = ();
 | 
						|
                @donly = grep { exists $dseen{$_} } @lonly;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                # Permission to remove all values
 | 
						|
                @donly = @lonly;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (@donly && @donly == keys %lseen) {
 | 
						|
            # If we are removing all ldap attributes do a remove or full delete
 | 
						|
            if (@uonly) {
 | 
						|
                $entry->replace($attr => [ @uonly ]);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                $entry->delete($attr => []);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            $entry->delete($attr => [ @donly ]) if @donly;
 | 
						|
            $entry->add($attr => [ @uonly ]) if @uonly;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    $data->{$dn}->{_done} = 1;
 | 
						|
    push @ldif, $entry;
 | 
						|
}
 | 
						|
$reader->done();
 | 
						|
 | 
						|
# Add missing records that didn't exist in ldap yet
 | 
						|
foreach $dn (grep { ! exists $data->{$_}->{_done} } sort keys %$data) {
 | 
						|
    my $entry = Net::LDAP::Entry->new;
 | 
						|
    $entry->dn($dn);
 | 
						|
 | 
						|
    foreach my $attr (sort keys %{$data->{$dn}}) {
 | 
						|
        # Skip the pseudo attributes
 | 
						|
        next if $attr =~ /^_/;
 | 
						|
 | 
						|
        my %seen = ();
 | 
						|
        @seen{ref $data->{$dn}->{$attr} ? @{$data->{$dn}->{$attr}} : ($data->{$dn}->{$attr})} = ();
 | 
						|
        $entry->add($attr => [ sort keys %seen ]) if keys %seen != 0;
 | 
						|
    }
 | 
						|
 | 
						|
    push @ldif, $entry;
 | 
						|
}
 | 
						|
 | 
						|
#------------------------------------------------------------
 | 
						|
# Update LDAP database entry.
 | 
						|
#------------------------------------------------------------
 | 
						|
my $ldap;
 | 
						|
if ($opt{update}) {
 | 
						|
    $ldap = Net::LDAP->new('localhost') or die "$@";
 | 
						|
    $ldap->bind( dn => "cn=root,$basedn", password => esmith::util::LdapPassword() );
 | 
						|
}
 | 
						|
 | 
						|
my $writer = Net::LDAP::LDIF->new( $opt{output}, 'w', onerror => 'undef', wrap => 0, sort => 1, change => $opt{diff} );
 | 
						|
foreach my $entry (sort dnsort @ldif) {
 | 
						|
    if ($opt{update} && ($entry->changetype ne 'modify' || @{$entry->{changes}}) ) {
 | 
						|
        my $result = $entry->update($ldap);
 | 
						|
        warn "Failure to ",$entry->changetype," ",$entry->dn,": ",$result->error,"\n" if $result->code;
 | 
						|
    }
 | 
						|
 | 
						|
    if ($writer->{change} || $entry->changetype !~ /modr?dn/) {
 | 
						|
        $writer->write_entry($entry);
 | 
						|
    }
 | 
						|
}
 |