summaryrefslogtreecommitdiffstats
path: root/ldap/admin/src/scripts/60upgradeschemafiles.pl
blob: 984973f5813730cb7d4fba4e99e1b40cd234869f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

use Mozilla::LDAP::LDIF;
use DSCreate qw(installSchema);

sub runinst {
    my ($inf, $inst, $dseldif, $conn) = @_;

    if (!$inf->{slapd}->{schema_dir} or (! -d $inf->{slapd}->{schema_dir})) {
        return ('error_reading_schema_dir', $inf->{slapd}->{schema_dir});
    }

    # these schema files are obsolete, or we want to replace
    # them with newer versions
    my @toremove = qw(00core.ldif 01common.ldif 05rfc2247.ldif 10presence.ldif 28pilot.ldif 50ns-directory.ldif 60mozilla.ldif);

    # these hashes will be used to check for obsolete schema
    # in 99user.ldif
    my %attrsbyname;
    my %attrsbyoid;
    my %objclassesbyname;
    my %objclassesbyoid;
    my $userschemaentry;

    # make a backup directory to store the deleted schema, then
    # don't really delete it, just move it to that directory
    my $mode = (stat($inf->{slapd}->{schema_dir}))[2];
    my $bakdir = $inf->{slapd}->{schema_dir} . ".bak";
    if (! -d $bakdir) {
        $! = 0; # clear
        mkdir $bakdir, $mode;
        if ($!) {
            return ('error_creating_directory', $bakdir, $!);
        }
    }

    my @errs;
    for my $file (@toremove) {
        my $oldname = $inf->{slapd}->{schema_dir} . "/" . $file;
        next if (! -f $oldname); # does not exist - skip - already (re)moved
        my $newname = "$bakdir/$file";
        $! = 0; # clear
        rename $oldname, $newname;
        if ($!) {
            push @errs, ["error_renaming_schema", $oldname, $newname, $!];
        }
    }

    # Remove obsolete schema from 99user.ldif. Compare by name and OID.
    if (!open( OLDUSERSCHEMA, $inf->{slapd}->{schema_dir} . "/99user.ldif")) {
        push @errs, ["error_reading_schema_file", $inf->{slapd}->{schema_dir} . "/99user.ldif", $!];
    } else {
        my $olduserschema = new Mozilla::LDAP::LDIF(*OLDUSERSCHEMA);

        # Find the cn=schema entry.
        while ($userschemaentry = readOneEntry $olduserschema) {
            my $dn = $userschemaentry->getDN();
            # The only entry should be cn=schema, but best to play it safe.
            next if ($dn ne "cn=schema");

            # create the attributeTypes hashes (name->value, oid->value)
            my @attrtypes = $userschemaentry->getValues("attributeTypes");
            foreach my $attrtype (@attrtypes) {
                # parse out the attribute name and oid
                if ($attrtype =~ /^\(\s*([\d\.]+)\s+NAME\s+'(\w+)'/) {
                    # normalize the attribute name
                    $attrsbyname{lc "$2"} = "$attrtype";
                    $attrsbyoid{"$1"} = "$attrtype";
                }
            }

            # create the objectClasses hashes (name->value, oid->value)
            my @objclasses = $userschemaentry->getValues("objectClasses");
            foreach my $objclass (@objclasses) {
                # parse out the objectclass name and oid
                if ($objclass =~ /^\(\s*([\d\.]+)\s+NAME\s+'(\w+)'/) {
                    # normalize the objectclass name
                    $objclassesbyname{lc "$2"} = "$objclass";
                    $objclassesbyoid{"$1"} = "$objclass";
                }
            }

            # We found the cn=schema entry, so there's no need
            # to look for more entries.
            last;
        }

        close OLDUSERSCHEMA;
    }

    for my $file (@toremove) {
        my $fullname = "$bakdir/$file";

        next if (! -f $fullname); # does not exist - skip - already (re)moved

        if (!open( OBSOLETESCHEMA, "$fullname")) {
            push @errs, ["error_reading_schema_file", $fullname, $!];
        } else {
            my $obsoleteschema = new Mozilla::LDAP::LDIF(*OBSOLETESCHEMA);

            # Find the cn=schema entry.
            while (my $entry = readOneEntry $obsoleteschema) {
                my $dn = $entry->getDN();
                # The only entry should be cn=schema, but best to play it safe.
                next if ($dn ne "cn=schema");

                # Check if any of the attributeTypes in this file
                # are defined in 99user.ldif and remove them if so.
                my @attrtypes = $entry->getValues("attributeTypes");
                foreach $attrtype (@attrtypes) {
                    # parse out the attribute name and oid
                    if ($attrtype =~ /^\(\s*([\d\.]+)\s+NAME\s+'(\w+)'/) {
                        # normalize the attribute name
                        if ($attrsbyname{lc "$2"}) {
                            $userschemaentry->removeValue("attributeTypes", $attrsbyname{lc "$2"});
                        } elsif ($attrsbyoid{"$1"}) {
                            $userschemaentry->removeValue("attributeTypes", $attrsbyoid{"$1"});
                        }
                    }
                }

                # Check if any of the objectClasses in this file
                # are defined in 99user.ldif and remove them if so.
                my @objclasses = $entry->getValues("objectClasses");
                foreach $objclass (@objclasses) {
                    # parse out the objectclass name and oid
                    if ($objclass =~ /^\(\s*([\d\.]+)\s+NAME\s+'(\w+)'/) {
                        # normalize the objectclass name
                        if ($objclassesbyname{lc "$2"}) {
                            $userschemaentry->removeValue("objectClasses", $objclassesbyname{lc "$2"});
                        } elsif ($objclassesbyoid{"$1"}) {
                            $userschemaentry->removeValue("objectClasses", $objclassesbyoid{"$1"});
                        }
                    }
                }
            }

            close OBSOLETESCHEMA;
        }
    }

    # Backup the original 99user.ldif
    $! = 0; # clear
    rename $inf->{slapd}->{schema_dir} . "/99user.ldif", "$bakdir/99user.ldif";
    if ($!) {
        push @errs, ["error_renaming_schema", $inf->{slapd}->{schema_dir} . "/99user.ldif", "$bakdir/99user.ldif", $!];
    }

    # Write the new 99user.ldif
    if (!open ( NEWUSERSCHEMA, ">" . $inf->{slapd}->{schema_dir} . "/99user.ldif")) {
        push @errs, ["error_writing_schema_file", $inf->{slapd}->{schema_dir} . "/99user.ldif", $!];
    } else {
        my $newuserschema = new Mozilla::LDAP::LDIF(*NEWUSERSCHEMA);
        writeOneEntry $newuserschema $userschemaentry;
        close NEWUSERSCHEMA;

        # Set permissions based off of the original 99user.ldif.
        my @stat = stat("$bakdir/99user.ldif");
        my $mode = $stat[2];
        my $uid = $stat[4];
        my $gid = $stat[5];
        chmod $mode, $inf->{slapd}->{schema_dir} . "/99user.ldif";
        chown $uid, $gid, $inf->{slapd}->{schema_dir} . "/99user.ldif";
    }

    # If we've encountered any errors up to this point, restore
    # the original schema.
    if (@errs) {
        # restore the original schema files
        for my $file (@toremove) {
            my $oldname = "$bakdir/$file";
            next if (! -f $oldname); # does not exist - not backed up
            my $newname = $inf->{slapd}->{schema_dir} . "/" . $file;
            next if (-f $newname); # not removed
            rename $oldname, $newname;
        }

        # Restore 99user.ldif. We overwrite whatever is there since
        # it is possible that we have modified it.
        if (-f "$bakdir/99user.ldif") {
                rename "$bakdir/99user.ldif", $inf->{slapd}->{schema_dir} . "/99user.ldif";
        }

        return @errs;
    }

    # after removing them, just add everything in the default
    # schema directory
    return installSchema($inf, 1);
}