diff -Nur -x '*.orig' -x '*.rej' e-smith-ldap-5.2.0/root/var/service/ldap/convert_ldif mezzanine_patched_e-smith-ldap-5.2.0/root/var/service/ldap/convert_ldif
--- e-smith-ldap-5.2.0/root/var/service/ldap/convert_ldif	2010-12-01 17:00:30.000000000 +0100
+++ mezzanine_patched_e-smith-ldap-5.2.0/root/var/service/ldap/convert_ldif	2010-12-01 16:48:15.000000000 +0100
@@ -1,67 +1,415 @@
-#! /usr/bin/perl
+#!/usr/bin/perl -w
 
 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';
+
+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 $olddomain = shift;
-my $newdomain = shift;
+my $c = esmith::ConfigDB->open_ro;
+my $a = esmith::AccountsDB->open_ro;
 
-my $ldif = Net::LDAP::LDIF->new( "/dev/stdin", "r", onerror => 'undef' );
-my $writer = Net::LDAP::LDIF->new("/dev/stdout", "w", onerror => 'undef' );
+my $auth = $c->get('ldap')->prop('Authentication') || 'disabled';
+my $schema = '/etc/openldap/schema/samba.schema';
 
-my $new = esmith::util::ldapBase($newdomain);
-my $old = esmith::util::ldapBase($olddomain);
+my $domain = $c->get('DomainName')->value;
+my $basedn = esmith::util::ldapBase($domain);
 
-while( not $ldif->eof())
-{
-    my $entry = $ldif->read_entry();
-    if ($ldif->error())
-    {
-        print "Error msg: ", $ldif->error(), "\n";
-        print "Error lines:\n", $ldif->error_lines(), "\n";
-        next;
-    }
-    next unless $entry;
-    my $dn = $entry->dn;
-    my @object_classes = $entry->get_value('objectClass');
-    my %object_classes = map { $_ => 1 } @object_classes;
-    if ($dn eq $old)
-    {
-        # this is the domain container object - objectClasses will be
-        # 'top' and 'domain'
-        my $dc = $new;
-        $dc =~ s/,.*//g;
-        $dc =~ s/^dc=//;
-        $entry->replace(dc => $dc);
-    }
-    if ($object_classes{group})
-    {
-        # We used to create group entries with invalid objectClass group
-        # - fix these if we find them
-        # possibly not required any more, but harmless
-        @object_classes = grep { $_ ne 'group' } @object_classes;
-        $entry->replace(objectClass => [ @object_classes, 'posixGroup' ] );
-    }
-    # do any other object transformations
-
-    # Update the mail attributes
-    if ($entry->exists('mail')){
-        my @newmails = ();
-        foreach ($entry->get_value('mail')){
-            $_ =~ s/\@$olddomain$/\@$newdomain/;
-            push (@newmails,$_);
+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=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} = $data->{$dn}->{gecos} = $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';
+
+        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/;
+            }
         }
-        $entry->replace(mail => [ @newmails ]);
     }
 
-    # Update basedb suffix
-    $dn =~ s/$old$/$new/;
+    $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 gecos/} =
+            map { $passwd[$_] ? $passwd[$_] : [] } (4,0,2,3,5,6,4);
+    }
+    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 == @lonly) {
+            # If we are removing all ldap only attributes do a remove or full delete
+            if (@uonly) {
+                $entry->replace($attr => [ @uonly ]);
+            }
+            else {
+                $entry->delete($attr => [ @donly == keys %lseen ? () : @donly ]);
+            }
+        }
+        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);
-    $writer->write($entry);
+
+    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);
+    }
 }
-$ldif->done( );
 
diff -Nur -x '*.orig' -x '*.rej' e-smith-ldap-5.2.0/root/var/service/ldap/run mezzanine_patched_e-smith-ldap-5.2.0/root/var/service/ldap/run
--- e-smith-ldap-5.2.0/root/var/service/ldap/run	2010-12-01 17:00:30.000000000 +0100
+++ mezzanine_patched_e-smith-ldap-5.2.0/root/var/service/ldap/run	2010-12-01 16:48:48.000000000 +0100
@@ -47,7 +47,7 @@
     if [ -e "$old_ldif" ]
     then
         grep -q "objectClass: dcObject" "$ldif" || /sbin/e-smith/expand-template /home/e-smith/db/ldap/ldif
-        perl ./convert_ldif $old_domain $domain < $old_ldif | \
+        perl ./convert_ldif -i - -o - < $old_ldif | \
             setuidgid ldap slapadd -c
     else
         if [ \! -e "$ldif" ]
