summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRich Megginson <rmeggins@redhat.com>2007-06-15 22:11:15 +0000
committerRich Megginson <rmeggins@redhat.com>2007-06-15 22:11:15 +0000
commit0046d389d23bd217e3109949064c33de07dbc017 (patch)
treefe3269e4ba221280d607cf4c54f17c596fa8adab
parent1698cb64cfd185dfee586169a3daef252e97f07a (diff)
downloadds-0046d389d23bd217e3109949064c33de07dbc017.tar.gz
ds-0046d389d23bd217e3109949064c33de07dbc017.tar.xz
ds-0046d389d23bd217e3109949064c33de07dbc017.zip
Resolves: bug 237356
Description: Move DS Admin Code into Admin Server Reviewed by: nhosoi (Thanks!) Fix Description: 1) Since we moved the o=NetscapeRoot code out of cfg_sspt.c, we no longer need to create the suffix and backend in create_instance.c 2) Added code to enable/disable dialogs e.g. for dialogs that can change the flow conditionally 3) Added code to allow the user to backup to the first prompt on a dialog, for dialogs with many prompts 4) Allow continuation lines in Resource files, instead of having to have embedded \n chars. This allows easier editing and layout. 5) Added an addSuffix function 6) Moved the register_servers.pl code from admin server into DS Util.pm and made it a little more general purpose. Platforms tested: RHEL4
-rw-r--r--ldap/admin/src/create_instance.c6
-rw-r--r--ldap/admin/src/ds_newinst.pl.in2
-rw-r--r--ldap/admin/src/scripts/Dialog.pm23
-rw-r--r--ldap/admin/src/scripts/DialogManager.pm35
-rw-r--r--ldap/admin/src/scripts/Resource.pm36
-rw-r--r--ldap/admin/src/scripts/Setup.pm.in17
-rw-r--r--ldap/admin/src/scripts/Util.pm534
-rw-r--r--ldap/admin/src/scripts/setup-ds.res.in6
8 files changed, 625 insertions, 34 deletions
diff --git a/ldap/admin/src/create_instance.c b/ldap/admin/src/create_instance.c
index 52e85bde..233628a3 100644
--- a/ldap/admin/src/create_instance.c
+++ b/ldap/admin/src/create_instance.c
@@ -3404,12 +3404,6 @@ char *ds_gen_confs(char *sroot, server_config_s *cf, char *cs_path)
fprintf(f, "cn: tasks\n");
fprintf(f, "\n");
- /* Entries for the ldbm instances and mapping tree */
- if ( cf->netscaperoot && !cf->use_existing_config_ds)
- {
- suffix_gen_conf(f, cf->netscaperoot, "NetscapeRoot");
- }
-
if (!cf->use_existing_user_ds)
{
suffix_gen_conf(f, cf->suffix, "userRoot");
diff --git a/ldap/admin/src/ds_newinst.pl.in b/ldap/admin/src/ds_newinst.pl.in
index 5c355656..817c4a93 100644
--- a/ldap/admin/src/ds_newinst.pl.in
+++ b/ldap/admin/src/ds_newinst.pl.in
@@ -189,7 +189,7 @@ usage("No arguments given") if (!@ARGV);
# process command line arguments
for (@ARGV) {
- if (/^(\w+).(\w+)=(.*)$/) { # e.g. section.param=value
+ if (/^(\w+)\.(\w+)=(.*)$/) { # e.g. section.param=value
$table{$1}->{$2} = $3;
} else { # file?
readInfFile($_);
diff --git a/ldap/admin/src/scripts/Dialog.pm b/ldap/admin/src/scripts/Dialog.pm
index 31893e7b..71fc48a7 100644
--- a/ldap/admin/src/scripts/Dialog.pm
+++ b/ldap/admin/src/scripts/Dialog.pm
@@ -106,6 +106,21 @@ sub isDisplayed {
return $self->{type} <= $self->{"manager"}->{type};
}
+sub isEnabled {
+ my $self = shift;
+ return !defined($self->{disabled});
+}
+
+sub enable {
+ my $self = shift;
+ delete $self->{disabled};
+}
+
+sub disable {
+ my $self = shift;
+ $self->{disabled} = 1;
+}
+
# each prompt looks like this:
# [ 'resource key', is pwd, hide ]
# The resource key is the string key of the resource
@@ -135,7 +150,7 @@ sub run {
my $prompt = $prompts[$index];
my $defaultans = $self->{defaultAns}($self, $index);
my $ans;
- if ($self->isDisplayed() && !$promtpt->[2]) {
+ if ($self->isDisplayed() && !$prompt->[2]) {
$ans = $self->{manager}->showPrompt($prompt->[0], $defaultans, $prompt->[1]);
} else {
$ans = $defaultans;
@@ -155,12 +170,14 @@ sub run {
# NOTE: user cannot BACK from prompt to prompt - BACK
# always means BACK to the previous dialog
$resp = $self->{handleResp}($self, $ans, $index);
- if ($resp == $DialogManager::SAME) {
+ if (($resp == $DialogManager::SAME) or ($resp == $DialogManager::FIRST)) {
if (!$self->isDisplayed()) {
$self->{manager}->alert('dialog_use_different_type');
$resp = $DialogManager::ERR;
- } else {
+ } elsif ($resp == $DialogManager::SAME) {
$index--; # reprompt
+ } else {
+ $index = -1; # reshow first prompt on dialog
}
} elsif ($resp == $DialogManager::ERR) {
last;
diff --git a/ldap/admin/src/scripts/DialogManager.pm b/ldap/admin/src/scripts/DialogManager.pm
index 9002b79e..7c68cdac 100644
--- a/ldap/admin/src/scripts/DialogManager.pm
+++ b/ldap/admin/src/scripts/DialogManager.pm
@@ -46,10 +46,11 @@ use Dialog;
use SetupLog;
# Dialog responses
-$BACK = -1;
-$SAME = 0;
-$NEXT = 1;
-$ERR = 2;
+$FIRST = -2; # go back to first prompt on a dialog
+$BACK = -1; # go back to previous dialog
+$SAME = 0; # reshow the same prompt or dialog
+$NEXT = 1; # go to the next dialog
+$ERR = 2; # fatal error
# The DialogManager controls the flow of the dialogs and contains context shared
# among all of the dialogs (resources, logs, current setup type, etc.)
@@ -199,18 +200,20 @@ sub run {
while (!$done) {
my $dialog = $self->{dialogs}->[$index];
- my $resp = $NEXT;
- $resp = $dialog->run();
- if ($resp == $BACK) {
- $incr = -1;
- } elsif ($resp == $NEXT) {
- $incr = 1;
- } elsif ($resp == $SAME) {
- $incr = 0;
- } else {
- $self->handleError($resp);
- $done = 1;
- $rc = 1;
+ if ($dialog->isEnabled()) {
+ my $resp = $NEXT;
+ $resp = $dialog->run();
+ if ($resp == $BACK) {
+ $incr = -1;
+ } elsif ($resp == $NEXT) {
+ $incr = 1;
+ } elsif (($resp == $SAME) or ($resp == $FIRST)) {
+ $incr = 0;
+ } else {
+ $self->handleError($resp);
+ $done = 1;
+ $rc = 1;
+ }
}
$index += $incr;
if ($index < 0) {
diff --git a/ldap/admin/src/scripts/Resource.pm b/ldap/admin/src/scripts/Resource.pm
index a8cb62a5..4bc52a74 100644
--- a/ldap/admin/src/scripts/Resource.pm
+++ b/ldap/admin/src/scripts/Resource.pm
@@ -74,19 +74,45 @@ sub read {
}
for my $filename (@{$self->{filenames}}) {
+ my $incontinuation = 0;
+ my $curkey;
open RES, $filename or die "Error: could not open resource file $filename: $!";
while (<RES>) {
- next if (/^\s*$/); # skip blank lines
- next if (/^\s*\#/); # skip comment lines
+ my $iscontinuation;
+ chop; # trim trailing newline
+ if (/^\s*$/) { # skip blank/empty lines
+ $incontinuation = 0;
+ next;
+ }
+ if (/^\s*\#/) { # skip comment lines
+ $incontinuation = 0;
+ next;
+ }
# read name = value pairs like this
# bol whitespace* name whitespace* '=' whitespace* value eol
# the value will include any trailing whitespace
- if (/^\s*(.*?)\s*=\s*(.*?)$/) {
- $self->{res}->{$1} = $2;
+ if (/\\$/) {
+ chop;
+ $iscontinuation = 1;
+ }
+ if ($incontinuation) {
+ $self->{res}->{$curkey} .= "\n" . $_;
+ } elsif (/^\s*(.*?)\s*=\s*(.*?)$/) {
# replace \n with real newline
- $self->{res}->{$1} =~ s/\\n/\n/g;
+ if ($curkey) {
+ $self->{res}->{$curkey} =~ s/\\n/\n/g;
+ }
+ $curkey = $1;
+ $self->{res}->{$curkey} = $2;
+ }
+ if ($iscontinuation) { # if line ends with a backslash, continue the data on the next line
+ $incontinuation = 1;
+ } else {
+ $incontinuation = 0;
}
}
+ # replace \n with real newline
+ $self->{res}->{$curkey} =~ s/\\n/\n/g;
close RES;
}
}
diff --git a/ldap/admin/src/scripts/Setup.pm.in b/ldap/admin/src/scripts/Setup.pm.in
index 67f6f21e..2e1ea646 100644
--- a/ldap/admin/src/scripts/Setup.pm.in
+++ b/ldap/admin/src/scripts/Setup.pm.in
@@ -147,7 +147,7 @@ sub new {
# arguments override those passed in via an inf file - this
# allows the reuse of .inf files with some parameters overridden
for (@ARGV) {
- if (/^(\w+).(\w+)=(.*)$/) { # e.g. section.param=value
+ if (/^(\w+)\.(\w+)=(.*)$/) { # e.g. section.param=value
$self->{inf}->{$1}->{$2} = $3;
} else { # error
print STDERR "Error: unknown command line option $_\n";
@@ -202,6 +202,21 @@ sub doExit {
exit 1;
}
+# get a list of the directory servers in configdir
+sub getDirServers {
+ my $self = shift;
+ if (!$self->{dirservers}) {
+ $self->{dirservers} = [];
+ for my $dir (glob("$self->{configdir}/slapd-*")) {
+ if (-d $dir) {
+ push @{$self->{dirservers}}, $dir;
+ }
+ }
+ }
+ return @{$self->{dirservers}};
+}
+
+
#############################################################################
# Mandatory TRUE return value.
#
diff --git a/ldap/admin/src/scripts/Util.pm b/ldap/admin/src/scripts/Util.pm
index 68aebd7b..5217c198 100644
--- a/ldap/admin/src/scripts/Util.pm
+++ b/ldap/admin/src/scripts/Util.pm
@@ -37,10 +37,18 @@
#
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);
-@EXPORT_OK = qw(portAvailable getAvailablePort isValidDN);
+@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;
@@ -79,4 +87,526 @@ sub isValidDN {
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;
diff --git a/ldap/admin/src/scripts/setup-ds.res.in b/ldap/admin/src/scripts/setup-ds.res.in
index 600b3ac3..80eb9fde 100644
--- a/ldap/admin/src/scripts/setup-ds.res.in
+++ b/ldap/admin/src/scripts/setup-ds.res.in
@@ -79,3 +79,9 @@ dialog_dssample_prompt = Do you want to install the sample entries?
dialog_dspopulate_text = You may wish to populate your new directory instance with some data.\n"You may already have a file in LDIF format to use or some suggested\nentries can be added. If you want to import entries from an LDIF\nfile, you may type in the full path and filename at the prompt. If\nyou want the setup program to add the suggested entries, type the\nword suggest at the prompt. The suggested entries are common\ncontainer entries under your specified suffix, such as ou=People and\nou=Groups, which are commonly used to hold the entries for the persons\nand groups in your organization. If you do not want to add any of\nthese entries, type the word none at the prompt.\n\n
dialog_dspopulate_prompt = Type the full path and filename, the word suggest, or the word none
dialog_dspopulate_error = The file '%s' was not found. Please choose another one.\n\n
+
+# ----------- miscellaneous ----------------
+backend_already_exists = A database backend with the name '%s' already exists. Config entry DN '%s'. Please choose another backend name. addSuffix can generate a unique backend name if you do not specify a backend name.\n\n
+suffix_already_exists = The suffix '%s' already exists. Config entry DN '%s'.\n\n
+error_creating_suffix_backend = Could not create the suffix '%s'. There was an error creating the backend database named '%s' for the suffix. Error: %s\n\n
+error_creating_suffix = Could not create the suffix '%s'. Error: %s\n\n