diff options
Diffstat (limited to 'ldap/admin/src/scripts/Util.pm')
| -rw-r--r-- | ldap/admin/src/scripts/Util.pm | 612 |
1 files changed, 0 insertions, 612 deletions
diff --git a/ldap/admin/src/scripts/Util.pm b/ldap/admin/src/scripts/Util.pm deleted file mode 100644 index 5217c198..00000000 --- a/ldap/admin/src/scripts/Util.pm +++ /dev/null @@ -1,612 +0,0 @@ -# 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) 2007 Red Hat, Inc. -# All rights reserved. -# END COPYRIGHT BLOCK -# - -package Util; - -use Mozilla::LDAP::Conn; -use Mozilla::LDAP::Utils qw(normalizeDN); -use Mozilla::LDAP::API; # Direct access to C API -use Mozilla::LDAP::LDIF; - -require Exporter; -@ISA = qw(Exporter); -@EXPORT = qw(portAvailable getAvailablePort isValidDN addSuffix getMappedEntries - process_maptbl check_and_add_entry getMappedEntries); -@EXPORT_OK = qw(portAvailable getAvailablePort isValidDN addSuffix getMappedEntries - process_maptbl check_and_add_entry getMappedEntries); - -use strict; - -use Socket; - -# return true if the given port number is available, false otherwise -sub portAvailable { - my $port = shift; - my $proto = getprotobyname('tcp'); - my $rc = socket(SOCK, PF_INET, SOCK_STREAM, $proto); - if ($rc == 1) { - $rc = bind(SOCK, sockaddr_in($port, INADDR_ANY)); - } - close(SOCK); - return $rc and ($rc == 1); -} - -# returns a randomly assigned port number, or -1 -# if not able to find an available port -sub getAvailablePort { - my $MINPORT = 1024; - my $MAXPORT = 65535; - - srand( time() ^ ($$ + ($$ << 15)) ); - while (1) { - my $port = $MINPORT + int(rand($MAXPORT-$MINPORT)); - - if (portAvailable($port)) { - return $port; - } - } -} - -sub isValidDN { - my $dn = shift; - return ($dn =~ /^[0-9a-zA-Z_-]+=.*$/); -} - -sub debug { - print @_, "\n"; -} - -# delete the subtree starting from the passed entry -sub delete_all -{ - my ($conn, $bentry) = @_; - my $sentry = $conn->search($bentry->{dn}, - "subtree", "(objectclass=*)", 0, ("dn")); - my @mystack = (); - while ($sentry) { - push @mystack, $sentry->getDN(); - $sentry = $conn->nextEntry(); - } - # reverse order - my $dn = pop @mystack; - while ($dn) { - $conn->delete($dn); - my $rc = $conn->getErrorCode(); - if ( $rc != 0 ) { - $conn->printError(); - print "ERROR: unable to delete entry $dn, error code: $rc\n"; - return 1; - } - $dn = pop @mystack; - } - return 0; -} - -my %ignorelist = ( - "modifytimestamp", "modifyTimestamp", - "createtimestamp", "createTimestamp", - "installationtimestamp", "installationTimestamp", - "creatorsname", "creatorsName", - "modifiersname", "modifiersName", - "numsubordinates", "numSubordinates" -); - -my %speciallist = ( - "uniquemember", 1 -); - -# compare 2 entries -# return 0 if they match 100% (exception: %ignorelist). -# return 1 if they match except %speciallist. -# return -1 if they do not match. -sub comp_entries -{ - my ($e0, $e1) = @_; - my $rc = 0; - foreach my $akey ( keys %{$e0} ) - { - next if ( $ignorelist{lc($akey)} ); - my $aval0 = $e0->{$akey}; - my $aval1 = $e1->{$akey}; - my $amin; - my $amax; - my $a0max = $#{$aval0}; - my $a1max = $#{$aval1}; - if ( $a0max != $a1max ) - { - if ( $speciallist{lc($akey)} ) - { - $rc = 1; - if ( $a0max < $a1max ) - { - $amin = $a0max; - $amax = $a1max; - } - else - { - $amin = $a1max; - $amax = $a0max; - } - } - else - { - $rc = -1; - return $rc; - } - } - my @sval0 = sort { $a cmp $b } @{$aval0}; - my @sval1 = sort { $a cmp $b } @{$aval1}; - for ( my $i = 0; $i <= $amin; $i++ ) - { - my $isspecial = -1; - if ( $sval0[$i] ne $sval1[$i] ) - { - if ( 0 > $isspecial ) - { - $isspecial = $speciallist{lc($akey)}; - } - if ( $isspecial ) - { - $rc = 1; - } - else - { - $rc = -1; - return $rc; - } - } - } - } - return $rc; -} - -# if the entry does not exist on the server, add the entry. -# otherwise, do nothing -# you can use this as the callback to getMappedEntries, so -# that for each entry in the ldif file being processed, you -# can call this subroutine to add or update the entry -# use like this: -# getMappedEntries($mapper, \@ldiffiles, \&check_and_add_entry, -# [$conn, $fresh, $verbose]); -# where $conn is a perldap Conn -# $fresh if true will update the entry if it exists -# $verbose prints out more info -sub check_and_add_entry -{ - my ($context, $aentry) = @_; - my $conn = $context->[0]; - my $fresh = $context->[1]; - my $verbose = $context->[2]; - my $sentry = $conn->search($aentry->{dn}, "base", "(objectclass=*)"); - do - { - my $needtoadd = 1; - my $needtomod = 0; - my $rval = -1; - if ( $sentry && !$fresh ) - { - $rval = comp_entries( $sentry, $aentry ); - } - if ( 0 == $rval && !$fresh ) - { - # the identical entry exists on the configuration DS. - # no need to add the entry. - $needtoadd = 0; - goto out; - } - elsif ( (1 == $rval) && !$fresh ) - { - $needtoadd = 0; - $needtomod = 1; - } - elsif ( $sentry && $sentry->{dn} ) - { - # $fresh || $rval == -1 - # an entry having the same DN exists, but the attributes do not - # match. remove the entry and the subtree underneath. - if ( $verbose ) - { - print "Deleting an entry dn: $sentry->{dn} ...\n"; - } - $rval = delete_all($conn, $sentry); - if ( 0 != $rval ) - { - return 0; - } - } - - if ( 1 == $needtoadd ) - { - $conn->add($aentry); - my $rc = $conn->getErrorCode(); - if ( $rc != 0 ) - { - print "ERROR: adding an entry $aentry->{dn} failed, error code: $rc\n"; - print "[entry]\n"; - $aentry->printLDIF(); - $conn->close(); - return 0; - } -# if ( $verbose ) -# { -# print "Entry $aentry->{dn} is added\n"; -# } - } - elsif ( 1 == $needtomod ) # $sentry exists - { - foreach my $attr ( keys %speciallist ) - { - foreach my $nval ( @{$aentry->{$attr}} ) - { - $sentry->addValue( $attr, $nval ); - } - } - $conn->update($sentry); - my $rc = $conn->getErrorCode(); - if ( $rc != 0 ) - { - print "ERROR: updating an entry $sentry->{dn} failed, error code: $rc\n"; - print "[entry]\n"; - $aentry->printLDIF(); - $conn->close(); - return 0; - } - } - if ( $sentry ) - { - $sentry = $conn->nextEntry(); # supposed to have no more entries - } - } until ( !$sentry ); -out: - return 1; -} - -# the default callback used with getMappedEntries -# just adds the given entry to the given list -sub cbaddent { - my $list = shift; - my $ent = shift; - push @{$list}, $ent; - return 1; -} - -# given a mapper and a list of LDIF files, produce a list of -# perldap Entry objects which have had their tokens subst-ed -# with values from the mapper -# An optional callback can be supplied. Each entry will be -# given to this callback. The callback should return a list -# of localizable errors. If no callback is supplied, the -# entries will be returned in a list. -# Arguments: -# mapper - a hash ref - the keys are the tokens to replace -# and the values are the replacements -# ldiffiles - an array ref - the list of LDIF files to -# operate on -# callback (optional) - a code ref - a ref to a subroutine -# that will be called with each entry - see below -# context (optional) - this will be passed as the first -# argument to your given callback - see below -# errs (optional) - an array ref - This is how errors -# are returned to the caller - see below -# Callback: -# The callback sub will be called for each entry after -# the entry has been converted. The callback will be -# called with the given context as the first argument -# and the Mozilla::LDAP::Entry as the second argument, -# and an errs array ref as the third argument. The -# callback should return true to continue processing, -# or false if a fatal error was encountered that should -# abort processing of any further. -# Errors: -# This function should return an array of errors in the -# format described below, for use with Resource::getText(). -# If the callback returns any errors -# Return: -# The return value is a list of entries. -# Example usage: -# sub handle_entries { -# my $context = shift; -# my $entry = shift; -# .... do something with entry .... -# .... if $context is Mozilla::LDAP::Conn, $conn->add($entry); ... -# .... report errors .... -# if ($fatalerror) { -# return 0; -# } else { -# return 1; -# } -# } -# $mapper = {foo => 'bar', baz => 'biff'}; -# @ldiffiles = ('foo.ldif', 'bar.ldif', ..., 'biff.ldif'); -# $conn = new Mozilla::LDAP::Conn(...); -# @entries = getMappedEntries($mapper, \@ldiffiles, \&handle_entries, $conn); -# Note that this will return 0 entries since a callback was used. -# The simpler example is this: -# @entries = getMappedEntries($mapper, \@ldiffiles); -# -sub getMappedEntries { - my $mapper = shift; - my $ldiffiles = shift; - my $callback = shift || \&cbaddent; # default - just add entry to @entries - my @entries = (); - my $context = shift || \@entries; - my $error; - - if (!ref($ldiffiles)) { - $ldiffiles = [ $ldiffiles ]; - } - - foreach my $ldiffile (@{$ldiffiles}) { - open(MYLDIF, "< $ldiffile") or die "Can't open $ldiffile : $!"; - my $in = new Mozilla::LDAP::LDIF(*MYLDIF); - debug("Processing $ldiffile ..."); - ENTRY: while (my $entry = Mozilla::LDAP::LDIF::readOneEntry($in)) { - # first, fix the DN - my $dn = $entry->getDN(); - my $origdn = $dn; - while ( $dn =~ /%([\w_-]+)%/ ) { - if (exists($mapper->{$1})) { - $dn =~ s{%([\w_-]+)%}{$mapper->{$1}}ge; - } else { - print "ERROR: \"$origdn\" mapped to \"$dn\".\n"; - print "The LDIF file $ldiffile contains a token $1 for which there is no mapper.\n"; - print "Please check $ldiffile and your mapper to make sure all tokens are handled correctly.\n"; - $error = 1; - last ENTRY; - } - } - $entry->setDN($dn); - # next, fix all of the values in all of the attributes - foreach my $attr (keys %{$entry}) { - my @newvalues = (); - foreach my $value ($entry->getValues($attr)) { - # Need to repeat to handle nested subst - my $origvalue = $value; - while ( $value =~ /%([\w_-]+)%/ ) { - if (exists($mapper->{$1})) { - $value =~ s{%([\w_-]+)%}{$mapper->{$1}}ge; - } else { - print "ERROR: \"$origvalue\" mapped to \"$value\".\n"; - print "The LDIF file $ldiffile contains a token $1 for which there is no mapper.\n"; - print "Please check $ldiffile and your mapper to make sure all tokens are handled correctly.\n"; - $error = 1; - last ENTRY; - } - } - push @newvalues, $value; - } - $entry->setValues( $attr, @newvalues ); - } - - if (!&{$callback}($context, $entry)) { - print "There was an error processing entry ", $entry->getDN(), "\n"; - print "Cannot continue processing entries.\n"; - $error = 1; - last ENTRY; - } - - } - close(MYLDIF); - last if ($error); # do not process any more ldiffiles if an error occurred - } - - return @entries; -} - -# you should only use this function if you know for sure -# that the suffix and backend do not already exist -# use addSuffix instead -sub newSuffixAndBackend { - my $context = shift; - my $suffix = shift; - my $bename = shift; - my $nsuffix = normalizeDN($suffix); - my @errs; - - my $dn = "cn=$bename, cn=ldbm database, cn=plugins, cn=config"; - my $entry = new Mozilla::LDAP::Entry(); - $entry->setDN($dn); - $entry->setValues('objectclass', 'top', 'extensibleObject', 'nsBackendInstance'); - $entry->setValues('cn', $bename); - $entry->setValues('nsslapd-suffix', $nsuffix); - $context->add($entry); - my $rc = $context->getErrorCode(); - if ($rc) { - return ('error_creating_suffix_backend', $suffix, $bename, $context->getErrorString()); - } - - $entry = new Mozilla::LDAP::Entry(); - $dn = "cn=\"$nsuffix\", cn=mapping tree, cn=config"; - $entry->setDN($dn); - $entry->setValues('objectclass', 'top', 'extensibleObject', 'nsMappingTree'); - $entry->setValues('cn', "\"$nsuffix\""); - $entry->setValues('nsslapd-state', 'backend'); - $entry->setValues('nsslapd-backend', $bename); - $context->add($entry); - $rc = $context->getErrorCode(); - if ($rc) { - return ('error_creating_suffix', $suffix, $context->getErrorString()); - } - - return (); -} - -sub findbecb { - my $entry = shift; - my $attrs = shift; - return $entry->hasValue('objectclass', $attrs->[0], 1) && - $entry->hasValue('cn', $attrs->[1], 1); -} - -sub findBackend { - my $context = shift; - my $bename = shift; - my $ent; - if (ref($context) eq 'Mozilla::LDAP::Conn') { - $ent = $context->search("cn=ldbm database,cn=plugins,cn=config", "one", - "(&(objectclass=nsBackendInstance)(cn=$bename)") - } else { - $ent = $context->search("cn=ldbm database,cn=plugins,cn=config", "one", - \&findbecb, ['nsBackendInstance', $bename]) - } -} - -sub findsuffixcb { - my $entry = shift; - my $attrs = shift; - return $entry->hasValue('cn', $attrs->[0], 1) || - $entry->hasValue('cn', $attrs->[1], 1); -} - -sub findSuffix { - my $context = shift; - my $suffix = shift; - my $nsuffix = normalizeDN($suffix); - my $ent; - if (ref($context) eq 'Mozilla::LDAP::Conn') { - $ent = $context->search("cn=mapping tree,cn=config", "one", - "(|(cn=\"$suffix\")(cn=\"$nsuffix\"))"); - } else { - $ent = $context->search("cn=mapping tree,cn=config", "one", - \&findsuffixcb, ["\"$suffix\"", "\"$nsuffix\""]) - } -} - -sub getUniqueBackendName { - my $context = shift; - my $bename = "backend"; - my $index = 0; - my $ent = findBackend($context, ($bename . $index)); - while ($ent) { - ++$index; - $ent = findBackend($context, ($bename . $index)); - } - - return $bename.$index; -} - -sub addSuffix { - my $context = shift; # Conn - my $suffix = shift; - my $bename = shift; # optional - my $ent; - - if ($bename && ($ent = findBackend($context, $bename))) { - return ('backend_already_exists', $bename, $ent->getDN()); - } - - if ($ent = findSuffix($context, $suffix)) { - return ('suffix_already_exists', $suffix, $ent->getDN()); - } - - if (!$bename) { - $bename = getUniqueBackendName($context); - } - - my @errs = newSuffixAndBackend($context, $suffix, $bename); - - return @errs; -} - -# process map table -# [map table sample] -# fqdn = FullMachineName -# hostname = `use Sys::Hostname; $returnvalue = hostname();` -# ds_console_jar ="%normbrand%-ds-%ds_version%.jar" -# -# * If the right-hand value is in ` (backquote), the value is eval'ed by perl. -# The output should be stored in $returnvalue to pass to the internal hash. -# * If the right-hand value is in " (doublequote), the value is passed as is. -# * If the right-hand value is not in any quote, the value should be found -# in either of the setup inf file (static) or the install inf file (dynamic). -# * Variables surrounded by @ (e.g., @admin_confdir@) are replaced with the -# system path at the compile time. -# * The right-hand value can contain variables surrounded by % (e.g., %asid%) -# which refers the right-hand value (key) of this map file. -# The %token% tokens are replaced in getMappedEntries -sub process_maptbl -{ - my ($mapper, @infdata) = @_; - - if (defined($mapper->{""})) { - $mapper = $mapper->{""}; # side effect of Inf with no sections - } - - KEY: foreach my $key (keys %{$mapper}) - { - my $value = $mapper->{$key}; - if ($value =~ /^\"/) - { - $value =~ tr/\"//d; # value is a regular double quoted string - remove quotes - $mapper->{$key} = $value; - } - elsif ($value =~ /^\`/) - { - $value =~ tr/\`//d; # value is a perl expression to eval - my $returnvalue; # set in eval expression - eval $value; - $mapper->{$key} = $returnvalue; # perl expression sets $returnvalue - } - else - { - # get the value from one of the Inf passed in - my $infsection; - foreach my $thisinf (@infdata) - { - foreach my $section0 (keys %{$thisinf}) - { - $infsection = $thisinf->{$section0}; - next if (!ref($infsection)); - if (defined($infsection->{$value})) - { - $mapper->{$key} = $infsection->{$value}; - next KEY; - } - } - } - if (!defined($infsection->{$value})) - { - print "ERROR: $value not found in the .inf files\n"; - return {}; - } - } - } - return $mapper; -} - -1; |
