#! /bin/sh
# $OpenLDAP$
## This work is part of OpenLDAP Software <http://www.openldap.org/>.
##
## Copyright 1998-2024 The OpenLDAP Foundation.
## All rights reserved.
##
## Redistribution and use in source and binary forms, with or without
## modification, are permitted only as authorized by the OpenLDAP
## Public License.
##
## A copy of this license is available in the file LICENSE in the
## top-level directory of the distribution or, alternatively, at
## <http://www.OpenLDAP.org/license.html>.

echo "running defines.sh"
. $SRCDIR/scripts/defines.sh

if test $BACKLDAP = ldapno; then
	echo "LDAP backend not available, test skipped"
	exit 0
fi

if test $RWM = rwmno; then
	echo "rwm (rewrite/remap) overlay not available, test skipped"
	exit 0
fi

if test $SYNCPROV = syncprovno; then 
	echo "Syncrepl provider overlay not available, test skipped"
	exit 0
fi 

RMTSUFFIX="dc=remote,$BASEDN"
RMTROOTDN="cn=Manager,$RMTSUFFIX"

RMTDIR=$TESTDIR/remote
PR1DIR=$TESTDIR/provider1
PR2DIR=$TESTDIR/provider2
RMTCONF=$RMTDIR/slapd.d
PR1CONF=$PR1DIR/slapd.d
PR2CONF=$PR2DIR/slapd.d

ENTRIES=$TESTDIR/entries.ldif
SYNC1OUT=$TESTDIR/syncrepl1.out
SYNC2OUT=$TESTDIR/syncrepl2.out

mkdir -p $RMTDIR $RMTCONF $RMTDIR/db
mkdir -p $PR1DIR $PR1CONF $PR1DIR/db
mkdir -p $PR2DIR $PR2CONF $PR2DIR/db

cd $TESTDIR

KILLPIDS=

$SLAPPASSWD -g -n > $CONFIGPWF

cat <<EOF > $CONFLDIF
dn: cn=config
objectClass: olcGlobal
cn: config

dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcRootPW:< file://$CONFIGPWF

dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema

include: file://$ABS_SCHEMADIR/core.ldif
include: file://$ABS_SCHEMADIR/cosine.ldif
include: file://$ABS_SCHEMADIR/nis.ldif
include: file://$ABS_SCHEMADIR/inetorgperson.ldif

dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulePath: $TESTWD/../servers/slapd/overlays
EOF

[ "$BACKENDTYPE" = mod ] && echo "olcModuleLoad: $TESTWD/../servers/slapd/back-$BACKEND/back_$BACKEND.la" >> $CONFLDIF

echo "Initializing remote configurations..."
cat $CONFLDIF - <<EOF | $SLAPADD -F $RMTCONF -n 0

dn: olcDatabase={1}$BACKEND,cn=config
objectClass: olcDatabaseConfig
${nullExclude}objectClass: olc${BACKEND}Config
olcDatabase: {1}$BACKEND
${nullExclude}olcDbDirectory: $RMTDIR/db
olcSuffix: $RMTSUFFIX
olcRootDN: $RMTROOTDN
olcRootPW: $PASSWD
EOF
RC=$?
if test $RC != 0 ; then
	echo "slapadd failed ($RC)!"
	exit $RC
fi

[ "$BACKLDAP" = ldapmod ] && echo "olcModuleLoad: $TESTWD/../servers/slapd/back-ldap/back_ldap.la" >> $CONFLDIF
[ "$RWM" = rwmmod ] && echo "olcModuleLoad: rwm.la" >> $CONFLDIF
[ "$SYNCPROV" = syncprovmod ] && echo "olcModuleLoad: syncprov.la" >> $CONFLDIF
cat <<EOF >> $CONFLDIF

dn: olcDatabase={1}ldap,cn=config
objectClass: olcDatabaseConfig
objectClass: olcLDAPConfig
olcDatabase: {1}ldap
olcSuffix: ou=remote,ou=users,$BASEDN
olcSubordinate: TRUE
olcDbURI: $URI1
olcDbIDAssertBind: bindmethod=simple
  binddn="$RMTROOTDN"
  credentials=$PASSWD
  mode=none
olcDbIDAssertAuthzFrom: dn.exact:$MANAGERDN
olcRootDN: $MANAGERDN

dn: olcOverlay={0}rwm,olcDatabase={1}ldap,cn=config
objectClass: olcOverlayConfig
objectClass: olcRwmConfig
olcOverlay: {0}rwm
olcRwmRewrite: rwm-suffixmassage "ou=users,$RMTSUFFIX"
EOF

echo "Initializing provider1 configurations..."
cat $CONFLDIF - <<EOF | $SLAPADD -F $PR1CONF -n 0

dn: olcDatabase={2}$BACKEND,cn=config
objectClass: olcDatabaseConfig
${nullExclude}objectClass: olc${BACKEND}Config
olcDatabase: {2}$BACKEND
${nullExclude}olcDbDirectory: $PR1DIR/db
olcSuffix: $BASEDN
olcRootDN: $MANAGERDN
olcRootPW: $PASSWD

dn: olcOverlay={0}syncprov,olcDatabase={2}$BACKEND,cn=config
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: {0}syncprov
EOF
RC=$?
if test $RC != 0 ; then
	echo "slapadd failed ($RC)!"
	exit $RC
fi

echo "Initializing provider2 configurations..."
cat $CONFLDIF - <<EOF | $SLAPADD -F $PR2CONF -n 0

dn: olcDatabase={2}$BACKEND,cn=config
objectClass: olcDatabaseConfig
${nullExclude}objectClass: olc${BACKEND}Config
olcDatabase: {2}$BACKEND
${nullExclude}olcDbDirectory: $PR2DIR/db
olcSuffix: $BASEDN
olcRootDN: $MANAGERDN
olcRootPW: $PASSWD

dn: olcOverlay={0}glue,olcDatabase={2}$BACKEND,cn=config
objectClass: olcOverlayConfig
objectClass: olcConfig
olcOverlay: {0}glue

dn: olcOverlay={1}syncprov,olcDatabase={2}$BACKEND,cn=config
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: {1}syncprov
EOF
RC=$?
if test $RC != 0 ; then
	echo "slapadd failed ($RC)!"
	exit $RC
fi

echo "Starting remote slapd on TCP/IP port $PORT1..."
cd $RMTDIR
$SLAPD -F slapd.d -h $URI1 -d $LVL > $LOG1 2>&1 &
PID=$!
if test $WAIT != 0 ; then
    echo PID $PID
    read foo
fi
KILLPIDS="$KILLPIDS $PID"
cd $TESTWD
sleep 1
echo "Using ldapsearch to check that remote slapd is running..."
for i in 0 1 2 3 4 5; do
	$LDAPSEARCH -s base -b "" -H $URI1 \
		'objectclass=*' > /dev/null 2>&1
	RC=$?
	if test $RC = 0 ; then
		break
	fi
	echo "Waiting 5 seconds for slapd to start..."
	sleep 5
done
if test $RC != 0 ; then
	echo "ldapsearch failed ($RC)!"
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Starting provider1 slapd on TCP/IP port $PORT2..."
cd $PR1DIR
$SLAPD -F slapd.d -h $URI2 -d $LVL > $LOG2 2>&1 &
PID=$!
if test $WAIT != 0 ; then
    echo PID $PID
    read foo
fi
KILLPIDS="$KILLPIDS $PID"
cd $TESTWD
sleep 1
echo "Using ldapsearch to check that provider1 slapd is running..."
for i in 0 1 2 3 4 5; do
	$LDAPSEARCH -s base -b "" -H $URI2 \
		'objectclass=*' > /dev/null 2>&1
	RC=$?
	if test $RC = 0 ; then
		break
	fi
	echo "Waiting 5 seconds for slapd to start..."
	sleep 5
done
if test $RC != 0 ; then
	echo "ldapsearch failed ($RC)!"
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Starting provider2 slapd on TCP/IP port $PORT3..."
cd $PR2DIR
$SLAPD -F slapd.d -h $URI3 -d $LVL > $LOG3 2>&1 &
PID=$!
if test $WAIT != 0 ; then
    echo PID $PID
    read foo
fi
KILLPIDS="$KILLPIDS $PID"
cd $TESTWD
sleep 1
echo "Using ldapsearch to check that provider2 slapd is running..."
for i in 0 1 2 3 4 5; do
	$LDAPSEARCH -s base -b "" -H $URI3 \
		'objectclass=*' > /dev/null 2>&1
	RC=$?
	if test $RC = 0 ; then
		break
	fi
	echo "Waiting 5 seconds for slapd to start..."
	sleep 5
done
if test $RC != 0 ; then
	echo "ldapsearch failed ($RC)!"
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Populating remote database entries..."
$LDAPADD -D "$RMTROOTDN" -H $URI1 -w $PASSWD <<EOF >> $TESTOUT 2>&1
dn: $RMTSUFFIX
objectClass: dcObject
objectClass: organization
dc: `echo $RMTSUFFIX | sed 's/^dc=\([^,]*\),.*/\1/'`
o: Example, Inc

dn: ou=users,$RMTSUFFIX
objectClass: organizationalUnit
ou: users
EOF
RC=$?
if test $RC != 0 ; then
	echo "ldapadd failed to populate remote database entries ($RC)!"
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

cat <<EOF > $ENTRIES
dn: $BASEDN
objectClass: dcObject
objectClass: organization
dc: example
o: Example, Inc

dn: ou=users,$BASEDN
objectClass: organizationalUnit
ou: users

dn: ou=local,ou=users,$BASEDN
objectClass: organizationalUnit
ou: local
EOF

echo "Populating provider1 database entries..."
$LDAPADD -D "$MANAGERDN" -H $URI2 -w $PASSWD < $ENTRIES >> $TESTOUT 2>&1
RC=$?
if test $RC != 0 ; then
	echo "ldapadd failed to populate provider1 database entries ($RC)!"
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Populating provider2 database entries..."
$LDAPADD -D "$MANAGERDN" -H $URI3 -w $PASSWD < $ENTRIES >> $TESTOUT 2>&1
RC=$?
if test $RC != 0 ; then
	echo "ldapadd failed to populate provider2 database entries ($RC)!"
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Starting refreshAndPersist search on provider1..."
$LDAPRSEARCH -D $MANAGERDN -H $URI2 -w $PASSWD -MM -E sync=rp -b $BASEDN '*' + 2>&1 > $SYNC1OUT &
PID=$!
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for refreshDone message..."
	sleep $SLEEP0
	if grep '^# refresh done, switching to persist stage' $SYNC1OUT; then
		awk '/^result:/{print; exit $2}' $SYNC1OUT
		RC=$?
		break
	fi
done
if test $RC != 0 ; then
	echo "refresh failed ($RC)!"
	kill $PID
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Using ldapadd to add local entry on provider1..."
$LDAPADD -D $MANAGERDN -H $URI2 -w $PASSWD <<EOF >> $TESTOUT 2>&1
dn: cn=local_user,ou=local,ou=users,$BASEDN
objectClass: person
cn: local_user
sn: local_user
userPassword: $PASSWD
description: add local_user
EOF
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for syncrepl to receive changes..."
	sleep $SLEEP0
	if grep -q '^dn: cn=local_user' $SYNC1OUT; then
		RC=0
		break
	fi
done
if test $RC != 0 ; then
	echo "syncrepl failed ($RC)!"
	kill $PID
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Using ldapmodify to modify local entry on provider1..."
$LDAPMODIFY -D $MANAGERDN -H $URI2 -w $PASSWD <<EOF >> $TESTOUT 2>&1
dn: cn=local_user,ou=local,ou=users,$BASEDN
changeType: modify
replace: description
description: modify local_user
EOF
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for syncrepl to receive changes..."
	sleep $SLEEP0
	if grep -q '^description: modify local_user' $SYNC1OUT; then
		RC=0
		break
	fi
done
if test $RC != 0 ; then
	echo "syncrepl failed ($RC)!"
	kill $PID
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Using ldapmodrdn to rename local entry on provider1..."
$LDAPMODRDN -D $MANAGERDN -H $URI2 -w $PASSWD -r <<EOF >> $TESTOUT 2>&1
cn=local_user,ou=local,ou=users,$BASEDN
cn=local_user1
EOF
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for syncrepl to receive changes..."
	sleep $SLEEP0
	if grep -q '^dn: cn=local_user1' $SYNC1OUT; then
		RC=0
		break
	fi
done
kill $PID
if test $RC != 0 ; then
	echo "syncrepl failed ($RC)!"
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Check that remote entries are NOT replicated..."
if grep 'ou=remote,' $SYNC1OUT; then
	echo "remote entries were unexpectedly replicated!"
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit 1
fi

echo "Starting refreshAndPersist search on provider2..."
$LDAPRSEARCH -D $MANAGERDN -H $URI3 -w $PASSWD -MM -E sync=rp -b $BASEDN '*' + 2>&1 > $SYNC2OUT &
PID=$!
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for refreshDone message..."
	sleep $SLEEP0
	if grep '^# refresh done, switching to persist stage' $SYNC2OUT; then
		awk '/^result:/{print; exit $2}' $SYNC2OUT
		RC=$?
		break
	fi
done
if test $RC != 0 ; then
	echo "refresh failed ($RC)!"
	kill $PID
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Using ldapadd to add local entry on provider2..."
$LDAPADD -D $MANAGERDN -H $URI3 -w $PASSWD <<EOF >> $TESTOUT 2>&1
dn: cn=local_user,ou=local,ou=users,$BASEDN
objectClass: person
cn: local_user
sn: local_user
userPassword: $PASSWD
description: add local_user
EOF
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for syncrepl to receive changes..."
	sleep $SLEEP0
	if grep -q '^dn: cn=local_user' $SYNC2OUT; then
		RC=0
		break
	fi
done
if test $RC != 0 ; then
	echo "syncrepl failed ($RC)!"
	kill $PID
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Using ldapmodify to modify local entry on provider2..."
$LDAPMODIFY -D $MANAGERDN -H $URI3 -w $PASSWD <<EOF >> $TESTOUT 2>&1
dn: cn=local_user,ou=local,ou=users,$BASEDN
changeType: modify
replace: description
description: modify local_user
EOF
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for syncrepl to receive changes..."
	sleep $SLEEP0
	if grep -q '^description: modify local_user' $SYNC2OUT; then
		RC=0
		break
	fi
done
if test $RC != 0 ; then
	echo "syncrepl failed ($RC)!"
	kill $PID
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Using ldapmodrdn to rename local entry on provider2..."
$LDAPMODRDN -D $MANAGERDN -H $URI3 -w $PASSWD -r <<EOF >> $TESTOUT 2>&1
cn=local_user,ou=local,ou=users,$BASEDN
cn=local_user1
EOF
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for syncrepl to receive changes..."
	sleep $SLEEP0
	if grep -q '^dn: cn=local_user1' $SYNC2OUT; then
		RC=0
		break
	fi
done
if test $RC != 0 ; then
	echo "syncrepl failed ($RC)!"
	kill $PID
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Using ldapadd to add remote entry on provider2..."
$LDAPADD -D $MANAGERDN -H $URI3 -w $PASSWD <<EOF >> $TESTOUT 2>&1
dn: cn=remote_user,ou=remote,ou=users,$BASEDN
objectClass: person
cn: remote_user
sn: remote_user
userPassword: $PASSWD
description: add remote_user
EOF
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for syncrepl to receive changes..."
	sleep $SLEEP0
	if grep -q '^dn: cn=remote_user' $SYNC2OUT; then
		RC=0
		break
	fi
done
if test $RC != 0 ; then
	echo "syncrepl failed ($RC)!"
	kill $PID
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Using ldapmodify to modify remote entry on provider2..."
$LDAPMODIFY -D $MANAGERDN -H $URI3 -w $PASSWD <<EOF >> $TESTOUT 2>&1
dn: cn=remote_user,ou=remote,ou=users,$BASEDN
changeType: modify
replace: description
description: modify remote_user
EOF
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for syncrepl to receive changes..."
	sleep $SLEEP0
	if grep -q '^description: modify remote_user' $SYNC2OUT; then
		RC=0
		break
	fi
done
if test $RC != 0 ; then
	echo "syncrepl failed ($RC)!"
	kill $PID
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

echo "Using ldapmodrdn to rename remote entry on provider2..."
$LDAPMODRDN -D $MANAGERDN -H $URI3 -w $PASSWD -r <<EOF >> $TESTOUT 2>&1
cn=remote_user,ou=remote,ou=users,$BASEDN
cn=remote_user1
EOF
RC=32
for i in 0 1 2 3 4 5; do
	echo "Waiting for syncrepl to receive changes..."
	sleep $SLEEP0
	if grep -q '^dn: cn=remote_user1' $SYNC2OUT; then
		RC=0
		break
	fi
done
kill $PID
if test $RC != 0 ; then
	echo "syncrepl failed ($RC)!"
	test $KILLSERVERS != no && kill -HUP $KILLPIDS
	exit $RC
fi

test $KILLSERVERS != no && kill -HUP $KILLPIDS

echo ">>>>> Test succeeded"
exit 0
