From ac9395d83669deb68c67a1ebbd361157067ad628 Mon Sep 17 00:00:00 2001
From: Kurt Zeilenga <kurt@openldap.org>
Date: Wed, 5 Jun 2002 22:07:20 +0000
Subject: [PATCH] updates from HEAD

---
 clients/tools/ldapsearch.c         | 106 +++++-
 doc/devel/args                     |  62 ++++
 doc/man/man1/ldapdelete.1          | 192 ++++++++---
 doc/man/man3/ldap_error.3          |   3 +-
 doc/man/man5/slapd-shell.5         |  37 +--
 doc/man/man5/slapd.conf.5          |  16 +
 libraries/libldap/controls.c       |   5 +-
 libraries/libldap/dnssrv.c         | 304 +++++++++++++++++
 libraries/libldap/error.c          |   9 -
 libraries/libldap/extended.c       |  10 -
 libraries/libldap/getdn.c          |   9 +-
 libraries/libldap/getentry.c       | 134 ++++++--
 libraries/libldap/references.c     | 146 ++++++++
 libraries/libldap/result.c         |   9 -
 libraries/libldap/sasl.c           |   9 -
 libraries/libldap/sort.c           |  84 ++---
 libraries/libldap/sortctrl.c       | 466 ++++++++++++++++++++++++++
 libraries/libldap/url.c            |  12 +-
 libraries/libldap/vlvctrl.c        | 277 ++++++++++++++++
 libraries/libldap_r/tpool.c        |   4 +
 libraries/liblutil/entropy.c       |  18 +-
 servers/slapd/at.c                 |  12 +-
 servers/slapd/back-ldbm/modify.c   |   1 -
 servers/slapd/back-shell/abandon.c |  22 +-
 servers/slapd/back-shell/add.c     |   1 -
 servers/slapd/back-shell/bind.c    |   1 -
 servers/slapd/back-shell/compare.c |   1 -
 servers/slapd/back-shell/delete.c  |   1 -
 servers/slapd/back-shell/modify.c  |   1 -
 servers/slapd/back-shell/modrdn.c  |   1 -
 servers/slapd/back-shell/search.c  |   1 -
 servers/slapd/back-shell/unbind.c  |   1 -
 servers/slapd/config.c             |  19 +-
 servers/slapd/daemon.c             |  80 ++++-
 servers/slapd/matchedValues.c      |  92 ++----
 servers/slapd/mr.c                 |   4 +-
 servers/slapd/oc.c                 | 512 +++++++++++++++++++++++++++++
 servers/slapd/passwd.c             |   8 +-
 servers/slapd/proto-slap.h         |   4 +-
 servers/slapd/result.c             |  78 ++++-
 servers/slapd/schema_prep.c        |  66 ++--
 servers/slapd/schemaparse.c        |  19 +-
 servers/slapd/slap.h               |  59 ++--
 servers/slapd/syntax.c             | 240 ++++++++++++++
 44 files changed, 2746 insertions(+), 390 deletions(-)
 create mode 100644 doc/devel/args
 create mode 100644 libraries/libldap/dnssrv.c
 create mode 100644 libraries/libldap/references.c
 create mode 100644 libraries/libldap/sortctrl.c
 create mode 100644 libraries/libldap/vlvctrl.c
 create mode 100644 servers/slapd/oc.c
 create mode 100644 servers/slapd/syntax.c

diff --git a/clients/tools/ldapsearch.c b/clients/tools/ldapsearch.c
index bf76dbdfdd..868ac0697f 100644
--- a/clients/tools/ldapsearch.c
+++ b/clients/tools/ldapsearch.c
@@ -186,9 +186,14 @@ main( int argc, char **argv )
 	int			referrals, timelimit, sizelimit, debug;
 	int		authmethod, version, want_bindpw;
 	LDAP		*ld = NULL;
+	int		valuesReturnFilter;
+	BerElement	*ber;
+	struct berval 	*bvalp;
+	char	*vrFilter  = NULL, *control  = NULL, *s;
+
 
 	infile = NULL;
-	debug = verbose = not = vals2tmp = referrals =
+	debug = verbose = not = vals2tmp = referrals = valuesReturnFilter =
 		attrsonly = manageDSAit = ldif = want_bindpw = 0;
 
 	lutil_log_initialize(argc, argv);
@@ -222,7 +227,7 @@ main( int argc, char **argv )
 
     prog = (prog = strrchr(argv[0], *LDAP_DIRSEP)) == NULL ? argv[0] : prog + 1;
 
-	while (( i = getopt( argc, argv, "Aa:b:F:f:Ll:S:s:T:tuz:"
+	while (( i = getopt( argc, argv, "Aa:b:E:F:f:Ll:S:s:T:tuz:"
 		"Cd:D:h:H:IkKMnO:p:P:QR:U:vw:WxX:Y:Z")) != EOF )
 	{
 	switch( i ) {
@@ -254,6 +259,47 @@ main( int argc, char **argv )
 		}
 		infile = strdup( optarg );
 		break;
+	case 'E': /* controls */
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -C incompatible with LDAPv%d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+
+		/* should be extended to support comma separated list of
+		 *	key/value pairs:  -E foo=123,bar=567
+		 */
+
+		control = strdup( optarg );
+		if ( (s = strchr( control, '=' )) == NULL ) {
+			return EXIT_FAILURE;
+		}
+
+		*s++ = '\0';
+		if ( strcasecmp( control, "mv" ) == 0 ) {
+			/* ValuesReturnFilter control */
+			if (valuesReturnFilter!=0) {
+				fprintf( stderr, "ValuesReturnFilter previously specified");
+				return EXIT_FAILURE;
+			}
+
+			if ( *s == '!' ){
+				s++;
+				valuesReturnFilter=2;
+			} else {
+				valuesReturnFilter=1;
+			}
+
+			vrFilter = s;
+			version = LDAP_VERSION3;
+			break;
+
+		} else {
+			fprintf( stderr, "Invalid control name: %s\n", control );
+			usage(prog);
+			return EXIT_FAILURE;
+		}
+
 	case 'F':	/* uri prefix */
 		if( urlpre ) free( urlpre );
 		urlpre = strdup( optarg );
@@ -829,24 +875,56 @@ main( int argc, char **argv )
 		}
 	}
 
-	if ( manageDSAit ) {
+	if ( manageDSAit || valuesReturnFilter ) {
 		int err;
-		LDAPControl c;
-		LDAPControl *ctrls[2];
-		ctrls[0] = &c;
-		ctrls[1] = NULL;
+		int i=0;
+		LDAPControl c1,c2;
+		LDAPControl *ctrls[3];
+		
+		if ( manageDSAit ) {
+			ctrls[i++]=&c1;
+			ctrls[i] = NULL;
+
+			c1.ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
+			c1.ldctl_value.bv_val = NULL;
+			c1.ldctl_value.bv_len = 0;
+			c1.ldctl_iscritical = manageDSAit > 1;
+		}
 
-		c.ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
-		c.ldctl_value.bv_val = NULL;
-		c.ldctl_value.bv_len = 0;
-		c.ldctl_iscritical = manageDSAit > 1;
+		if ( valuesReturnFilter ) {
+			struct berval *bvalp;
+			ctrls[i++]=&c2;
+			ctrls[i] = NULL;
 
+			c2.ldctl_oid = LDAP_CONTROL_VALUESRETURNFILTER;
+			c2.ldctl_iscritical = valuesReturnFilter > 1;
+		    
+		        if (( ber = ber_alloc_t(LBER_USE_DER)) == NULL ) 
+				exit( EXIT_FAILURE );
+
+		    	if ( err = put_vrFilter(ber, vrFilter)==-1 ) {
+				ber_free( ber, 1 );
+				fprintf( stderr, "Bad ValuesReturnFilter: %s\n", vrFilter );
+				exit( EXIT_FAILURE );
+			}
+
+			if ( ber_flatten( ber, &bvalp ) == LBER_ERROR ) 
+				return LDAP_NO_MEMORY;
+
+			c2.ldctl_value=(*bvalp);
+
+		}
+	
 		err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
 
+		ber_bvfree(bvalp);
+		ber_free( ber, 1 );
+
 		if( err != LDAP_OPT_SUCCESS ) {
-			fprintf( stderr, "Could not set ManageDSAit %scontrol\n",
-				c.ldctl_iscritical ? "critical " : "" );
-			if( c.ldctl_iscritical ) {
+			fprintf( stderr, "Could not set %scontrols\n",
+				(c1.ldctl_iscritical || c2.ldctl_iscritical)
+				? "critical " : "" );
+			if( c1.ldctl_iscritical && c2.ldctl_iscritical ) {
 				exit( EXIT_FAILURE );
 			}
 		}
diff --git a/doc/devel/args b/doc/devel/args
new file mode 100644
index 0000000000..ea02cfa4f4
--- /dev/null
+++ b/doc/devel/args
@@ -0,0 +1,62 @@
+Tools           ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+ldapdelete       *CDE *HI*K M*OPQR  U*WXYZ  cdef*h**k *n*p*    vwx*
+ldapmodify       *CDEF*HI*K M*OPQR  U*WXYZabcdef*h**k *n*p*r t vwx*
+ldapmodrdn       *CDE *HI*K M*OPQR  U*WXYZ  cdef*h**k *n*p*rs  vwx*
+ldappasswd      A*CDE *HI*   *O QRS U*WXYZa  de *h**  * * * s  vwx*  
+ldapsearch      A*CDE *HI*KLM*OPQRSTU*WXYZab*def*h**kl*n*p* stuvwx*z
+
+Other Clients   ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+fax500                               *         f h    m
+finger                               *      c  f  i  l  p    t   x
+go500                   I            *     bcd f     l  p    t   x
+go500gw                 I      P     *    a cd f h   l  p    t   x
+mail500           C                  *       d f h   lm        v
+rcpt500                             U*    abc  f h   l  p   st     z
+rp500                                *    ab d f                 x z
+ud                 D                 *      cd f     l  p   s uv
+
+
+* reserved
+	GJNgijmoqy
+
+* General flags:
+	-C Chase Referrals
+	-D Bind DN
+	-E CommandSpecific Extensions	(e.g., -E <[!]oid[=options]>*)
+	-e General Extensions			(e.g., -e <[!]oid[=options]>*)
+	-H URI
+	-P protocol version
+	-V version information
+	-W prompt for bind password
+	-d debug
+	-h host
+	-n no-op
+	-p port
+	-v verbose
+	-w Bind password
+
+
+* LDAPv3 Only 
+	-x simple bind (not recommended excepting for
+		anonymous access, w/ -ZZ, or with ldaps://.
+
+	-M ManageDSAIT
+	-Z StartTLS
+
+	-Y SASL Mechanism (defaults to "best")
+	-R SASL Realm (defaults to empty)
+	-O SASL Security Options (defaults to "noanonymous,noplain")
+	-U SASL Authentication Identity (defaults to USER)
+	-X SASL Authorization Identity (defaults to empty)
+
+	-I SASL interactive mode (default: automatic)
+	-Q SASL quiet mode (default: automatic)
+
+
+* LDAPv2+ Only (DEPRECATED)
+	-K LDAPv2 Kerberos Bind (Step 1 only)
+	-k LDAPv2 Kerberos Bind
+
+
+---
+$OpenLDAP$
diff --git a/doc/man/man1/ldapdelete.1 b/doc/man/man1/ldapdelete.1
index ea2f4726c2..1969b20cc3 100644
--- a/doc/man/man1/ldapdelete.1
+++ b/doc/man/man1/ldapdelete.1
@@ -1,20 +1,61 @@
-.TH LDAPDELETE 1 "13 November 1995" "U-M LDAP LDVERSION"
+.TH LDAPDELETE 1 "20 August 2001" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2002 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
 .SH NAME
-ldapdelete \- ldap delete entry tool
+ldapdelete \- LDAP delete entry tool
 .SH SYNOPSIS
 .B ldapdelete
-.B [\-n]
-.B [\-v]
-.B [\-k]
-.B [\-K]
-.B [\-c]
-.B [\-d debuglevel]
-.B [\-f file]
-.B [\-D binddn]
-.B [\-w passwd]
-.B [\-h ldaphost]
-.B [\-p ldapport]
-.B [dn]...
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-k ]
+[\c
+.BR \-K ]
+[\c
+.BR \-c ]
+[\c
+.BR \-C ]
+[\c
+.BR \-M[M] ]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BI \-f \ file\fR]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BI \-h \ ldaphost\fR]
+[\c
+.BI \-P \ 2\fR\||\|\fI3\fR]
+[\c
+.BI \-p \ ldapport\fR]
+[\c
+.BR \-O \ security-properties ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BR \-x ]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z[Z] ]
+[\c
+.IR dn ]...
 .SH DESCRIPTION
 .I ldapdelete
 is a shell-accessible interface to the
@@ -23,9 +64,10 @@ library call.
 .LP
 .B ldapdelete
 opens a connection to an LDAP server, binds, and deletes one or more
-entries.  If one or more \fIdn\fP arguments are provided, entries with
-those Distinguished Names are deleted.  Each \fIdn\fP should be a
-string-represented DN as defined in RFC 1779.  If no \fIdn\fP arguments
+entries.  If one or more \fIDN\fP arguments are provided, entries with
+those Distinguished Names are deleted.  Each \fIDN\fP should be provided
+using the LDAPv3 string representation as defined in RFC 2253.
+If no \fIdn\fP arguments
 are provided, a list of DNs is read from standard input (or from
 \fIfile\fP if the -f flag is used).
 .SH OPTIONS
@@ -38,16 +80,19 @@ debugging in conjunction with -v.
 Use verbose mode, with many diagnostics written to standard output.
 .TP
 .B \-k
-Use Kerberos authentication instead of simple authentication.  It is
+Use Kerberos IV authentication instead of simple authentication.  It is
 assumed that you already have a valid ticket granting ticket. This option
 only has effect if
 . B ldapdelete
-is compiled with KERBEROS defined.
+is compiled with Kerberos support.
 .TP
 .B \-K
-Same as \-k, but only does step 1 of the kerberos bind.  This is useful
+Same as \-k, but only does step 1 of the Kerberos IV bind.  This is useful
 when connecting to a slapd and there is no x500dsa.hostname principal
-registered with your kerberos servers.
+registered with your Kerberos Domain Controller(s).
+.TP
+.B \-C
+Automatically chase referrals.
 .TP
 .B \-c
 Continuous operation mode.  Errors  are  reported,  but
@@ -55,55 +100,110 @@ Continuous operation mode.  Errors  are  reported,  but
 will  continue  with  deletions.   The default is to exit after
 reporting an error.
 .TP
-.B \-d debuglevel
+.B \-M[M]
+Enable manage DSA IT control.
+.B \-MM
+makes control critical.
+.TP
+.BI \-d \ debuglevel
 Set the LDAP debugging level to \fIdebuglevel\fP.
 .B ldapdelete
 must be compiled with LDAP_DEBUG defined for this option to have any effect.
 .TP
-.B \-f file
-Read a series of lines from \fIfile\fP, performing one LDAP search for
-each line.  In this case, the \fIfilter\fP given on the command line
-is treated as a pattern where the first occurrence of \fB%s\fP is
-replaced with a line from \fIfile\fP.
+.BI \-f \ file
+Read a series of DNs from \fIfile\fP, one per line, performing an
+LDAP delete for each.
+.TP
+.B \-x 
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
 .TP
-.B \-D binddn
-Use \fIbinddn\fP to bind to the X.500 directory. \fIbinddn\fP should be
-a string-represented DN as defined in RFC 1779.
+.B \-W
+Prompt for simple authentication.
+This is used instead of specifying the password on the command line.
 .TP
-.B \-w passwd
+.BI \-w \ passwd
 Use \fIpasswd\fP as the password for simple authentication.
 .TP
-.B \-h ldaphost
+.BI \-H \ ldapuri
+Specify URI(s) referring to the ldap server(s).
+.TP
+.BI \-h \ ldaphost
 Specify an alternate host on which the ldap server is running.
+Deprecated in favor of -H.
 .TP
-.B \-p ldapport
+.BI \-p \ ldapport
 Specify an alternate TCP port where the ldap server is listening.
+Deprecated in favor of -H.
+.TP
+.BI \-P \ 2\fR\||\|\fI3
+Specify the LDAP protocol version to use.
+.TP
+.B \-r
+Do a recursive delete.  If the DN specified isn't a leaf, its
+children, and all their children are deleted down the tree.  No
+verification is done, so if you add this switch, ldapdelete will
+happily delete large portions of your tree.  Use with care.
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.B \-I
+Enable SASL Interactive mode.  Always prompt.  Default is to prompt
+only as needed.
+.TP
+.B \-Q
+Enable SASL Quiet mode.  Never prompt.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the identity depends on the
+actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.B dn:\c
+.I <distinguished name>
+or
+.B u:\c
+.I <username>
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication. If it's not
+specified, the program will choose the best mechanism the server knows.
+.TP
+.B \-Z[Z]
+Issue StartTLS (Transport Layer Security) extended operation. If you use
+.B \-ZZ\c
+, the command will require the operation to be successful.
 .SH EXAMPLE
 The following command:
 .LP
 .nf
-    ldapdelete "cn=Delete Me, o=University of Michigan, c=US"
+    ldapdelete "cn=Delete Me,dc=example,dc=com"
 .fi
 .LP
-will attempt to delete the entry named with commonName "Delete Me"
-directly below the University of Michigan organizational entry.  Of
-course it would probably be necessary to supply a \fIbinddn\fP and
-\fIpasswd\fP for deletion to be allowed (see the -D and -w options).
+will attempt to delete the entry named "cn=Delete Me,dc=example,dc=com".
+Of course it would probably be necessary to supply authentication
+credentials.
 .SH DIAGNOSTICS
 Exit status is 0 if no errors occur.  Errors result in a non-zero exit
 status and a diagnostic message being written to standard error.
 .SH "SEE ALSO"
+.BR ldap.conf (5),
 .BR ldapadd (1),
 .BR ldapmodify (1),
 .BR ldapmodrdn (1),
 .BR ldapsearch (1),
 .BR ldap (3),
 .BR ldap_delete (3)
-.LP
-Kille, S.,
-.IR "A String Representation of Distinguished Names",
-.SM RFC
-1779,
-ISODE Consortium, March 1995.
-.SH BUGS
-There is no interactive mode, but there probably should be.
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.B	OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B	OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.  
diff --git a/doc/man/man3/ldap_error.3 b/doc/man/man3/ldap_error.3
index da99aa6231..f19e7ff6e3 100644
--- a/doc/man/man3/ldap_error.3
+++ b/doc/man/man3/ldap_error.3
@@ -193,8 +193,7 @@ An invalid filter was supplied to ldap_search() (e.g., unbalanced
 parentheses).
 .TP
 .SM LDAP_PARAM_ERROR
-An ldap routine was called with a bad parameter (e.g., a NULL ld
-pointer, etc.).
+An ldap routine was called with a bad parameter.
 .TP
 .SM LDAP_NO_MEMORY
 An memory allocation (e.g., malloc(3) or other dynamic memory
diff --git a/doc/man/man5/slapd-shell.5 b/doc/man/man5/slapd-shell.5
index 0beee6d657..a880f68c6a 100644
--- a/doc/man/man5/slapd-shell.5
+++ b/doc/man/man5/slapd-shell.5
@@ -15,10 +15,8 @@ make it easy to tie an existing database to the
 front-end.
 .SH WARNING
 .B "This backend's calling conventions have changed since OpenLDAP 2.0."
-The operations receive a new "opid:" (operation ID) line, to be used
-instead of "msgid:".
-The "msgid:" line will be removed in a future version.
-Also, abandon now gets a new "abandonid:" line.
+The abandon operation now gets a new "pid:" line.
+The "msgid:" lines will be removed in a future version.
 .SH CONFIGURATION
 These
 .B slapd.conf
@@ -36,17 +34,15 @@ Each option is followed by the input lines that the program receives:
 .B abandon  <pathname> <argument>...
 .nf
 ABANDON
-opid: <operation ID>
 msgid: <message ID of operation to abandon>
 <repeat { "suffix:" <database suffix DN> }>
-abandonid: <operation ID of operation to abandon>
+pid: <process ID of operation to abandon>
 .fi
 .TP
 .B add      <pathname> <argument>...
 .nf
 ADD
-opid: <operation ID>
-msgid: <message ID>
+msgid: <message id>
 <repeat { "suffix:" <database suffix DN> }>
 <entry in LDIF format>
 .fi
@@ -54,8 +50,7 @@ msgid: <message ID>
 .B bind     <pathname> <argument>...
 .nf
 BIND
-opid: <operation ID>
-msgid: <message ID>
+msgid: <message id>
 <repeat { "suffix:" <database suffix DN> }>
 dn: <DN>
 method: <method number>
@@ -66,8 +61,7 @@ cred: <credentials>
 .B compare  <pathname> <argument>...
 .nf
 COMPARE
-opid: <operation ID>
-msgid: <message ID>
+msgid: <message id>
 <repeat { "suffix:" <database suffix DN> }>
 dn: <DN>
 <attribute>: <value>
@@ -76,8 +70,7 @@ dn: <DN>
 .B delete   <pathname> <argument>...
 .nf
 DELETE
-opid: <operation ID>
-msgid: <message ID>
+msgid: <message id>
 <repeat { "suffix:" <database suffix DN> }>
 dn: <DN>
 .fi
@@ -85,8 +78,7 @@ dn: <DN>
 .B modify   <pathname> <argument>...
 .nf
 MODIFY
-opid: <operation ID>
-msgid: <message ID>
+msgid: <message id>
 <repeat { "suffix:" <database suffix DN> }>
 dn: <DN>
 <repeat {
@@ -99,8 +91,7 @@ dn: <DN>
 .B modrdn   <pathname> <argument>...
 .nf
 MODRDN
-opid: <operation ID>
-msgid: <message ID>
+msgid: <message id>
 <repeat { "suffix:" <database suffix DN> }>
 dn: <DN>
 newrdn: <new RDN>
@@ -111,8 +102,7 @@ deleteoldrdn: <0 or 1>
 .B search   <pathname> <argument>...
 .nf
 SEARCH
-opid: <operation ID>
-msgid: <message ID>
+msgid: <message id>
 <repeat { "suffix:" <database suffix DN> }>
 base: <base DN>
 scope: <0-2, see ldap.h>
@@ -127,16 +117,11 @@ attrs: <"all" or space-separated attribute list>
 .B unbind   <pathname> <argument>...
 .nf
 UNBIND
-opid: <operation ID>
-msgid: <message ID>
+msgid: <message id>
 <repeat { "suffix:" <database suffix DN> }>
 dn: <bound DN>
 .fi
 .LP
-An
-.I operation ID
-is a "connection ID/message ID" string identifying an operation.
-.LP
 Note that you need only supply configuration lines for those commands you
 want the backend to handle.
 Operations for which a command is not supplied will be refused with an
diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
index 3c9ed2bc52..9a22026da7 100644
--- a/doc/man/man5/slapd.conf.5
+++ b/doc/man/man5/slapd.conf.5
@@ -204,6 +204,22 @@ disables Start TLS from forcing session to anonymous status (see also
 disables StartTLS if authenticated (see also
 .BR tls_2_anon ).
 .TP
+.B gentlehup { on | off }
+A SIGHUP signal will only cause a 'gentle' shutdown-attempt:
+.B Slapd
+will stop listening for new connections, but will not close the
+connections to the current clients.  It terminates when all clients
+have closed their connections (if they ever do), or \- as before \-
+if it receives a SIGTERM signal.  This can be useful if you wish to
+terminate the server and start a new
+.B slapd
+server
+.B with another database,
+without disrupting the currently active clients.
+The default is off.  You may wish to use
+.B idletimeout
+along with this option.
+.TP
 .B idletimeout <integer>
 Specify the number of seconds to wait before forcibly closing
 an idle client connection.  A idletimeout of 0 disables this
diff --git a/libraries/libldap/controls.c b/libraries/libldap/controls.c
index 9925357b0c..76c2799236 100644
--- a/libraries/libldap/controls.c
+++ b/libraries/libldap/controls.c
@@ -413,9 +413,8 @@ ldap_create_control(
 	LDAPControl *ctrl;
 	struct berval *bvalp;
 
-	if ( requestOID == NULL || ctrlp == NULL ) {
-		return LDAP_PARAM_ERROR;
-	}
+	assert( requestOID != NULL );
+	assert( ctrlp != NULL );
 
 	ctrl = (LDAPControl *) LDAP_MALLOC( sizeof(LDAPControl) );
 	if ( ctrl == NULL ) {
diff --git a/libraries/libldap/dnssrv.c b/libraries/libldap/dnssrv.c
new file mode 100644
index 0000000000..1a763c9b6e
--- /dev/null
+++ b/libraries/libldap/dnssrv.c
@@ -0,0 +1,304 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+/*
+ * locate LDAP servers using DNS SRV records.
+ * Location code based on MIT Kerberos KDC location code.
+ */
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/param.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+/* Sometimes this is not defined. */
+#ifndef T_SRV
+#define T_SRV            33
+#endif				/* T_SRV */
+
+int ldap_dn2domain(
+	LDAP_CONST char *dn_in,
+	char **domainp)
+{
+	int i;
+	char *domain = NULL;
+	char **dn;
+
+	if( dn_in == NULL || domainp == NULL ) {
+		return -1;
+	}
+
+	dn = ldap_explode_dn( dn_in, 0 );
+
+	if( dn == NULL ) {
+		return -2;
+	}
+
+	for( i=0; dn[i] != NULL; i++ ) {
+		char ** rdn = ldap_explode_rdn( dn[i], 0 );
+
+		if( rdn == NULL || *rdn == NULL ) {
+			LDAP_FREE( rdn );
+			LDAP_FREE( domain );
+			LDAP_VFREE( dn );
+			return -3;
+		}
+
+
+		if( rdn[1] == NULL ) {
+			/*
+			 * single-valued RDN
+			 */
+			char *dc;
+
+#define LDAP_DC "dc="
+#define LDAP_DCOID "0.9.2342.19200300.100.1.25="
+
+			if( strncasecmp( rdn[0],
+				LDAP_DC, sizeof(LDAP_DC)-1 ) == 0 )
+			{
+				dc = &rdn[0][sizeof(LDAP_DC)-1];
+
+			} else if( strncmp( rdn[0],
+				LDAP_DCOID, sizeof(LDAP_DCOID)-1 ) == 0 )
+			{
+				dc = &rdn[0][sizeof(LDAP_DCOID)-1];
+
+			} else {
+				dc = NULL;
+			}
+
+			if( dc != NULL ) {
+				char *ndomain;
+
+				if( *dc == '\0' ) {
+					/* dc value is empty! */
+					LDAP_FREE( rdn );
+					LDAP_FREE( domain );
+					LDAP_VFREE( dn );
+					LDAP_VFREE( rdn );
+					return -4;
+				}
+
+				ndomain = LDAP_REALLOC( domain,
+					( domain == NULL ? 0 : strlen(domain) )
+					+ strlen(dc) + sizeof(".") );
+
+				if( ndomain == NULL ) {
+					LDAP_FREE( rdn );
+					LDAP_FREE( domain );
+					LDAP_VFREE( dn );
+					LDAP_VFREE( rdn );
+					return -5;
+				}
+
+				if( domain == NULL ) {
+					ndomain[0] = '\0';
+				} else {
+					strcat( ndomain, "." );
+				}
+
+				strcat( ndomain, dc );
+
+				domain = ndomain;
+				continue;
+			}
+		}
+
+		/*
+		 * multi-valued RDN or fall thru
+		 */
+
+		LDAP_VFREE( rdn );
+		LDAP_FREE( domain );
+		domain = NULL;
+	} 
+
+	if( domain != NULL &&  *domain == '\0' ) {
+		LDAP_FREE( domain );
+		domain = NULL;
+	}
+
+	*domainp = domain;
+	return 0;
+}
+
+int ldap_domain2dn(
+	LDAP_CONST char *domain_in,
+	char **dnp)
+{
+    char *domain, *s, *tok_r, *dn;
+    size_t loc;
+
+    if (domain_in == NULL || dnp == NULL) {
+	return LDAP_NO_MEMORY;
+    }
+    domain = LDAP_STRDUP(domain_in);
+    if (domain == NULL) {
+	return LDAP_NO_MEMORY;
+    }
+    dn = NULL;
+    loc = 0;
+
+    for (s = ldap_pvt_strtok(domain, ".", &tok_r);
+	 s != NULL;
+	 s = ldap_pvt_strtok(NULL, ".", &tok_r)) {
+	size_t len = strlen(s);
+
+	dn = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len );
+	if (dn == NULL) {
+	    LDAP_FREE(domain);
+	    return LDAP_NO_MEMORY;
+	}
+	if (loc > 0) {
+	    /* not first time. */
+	    strcpy(dn + loc, ",");
+	    loc++;
+	}
+	strcpy(dn + loc, "dc=");
+	loc += sizeof("dc=")-1;
+
+	strcpy(dn + loc, s);
+	loc += len;
+    }
+
+    LDAP_FREE(domain);
+
+    *dnp = dn;
+
+    return LDAP_SUCCESS;
+}
+
+/*
+ * Lookup and return LDAP servers for domain (using the DNS
+ * SRV record _ldap._tcp.domain).
+ */
+int ldap_domain2hostlist(
+	LDAP_CONST char *domain,
+	char **list )
+{
+#ifdef HAVE_RES_QUERY
+    char *request;
+    char *hostlist = NULL;
+    int rc, len, cur = 0;
+    unsigned char reply[1024];
+
+	assert( domain != NULL );
+	assert( list != NULL );
+
+	if( *domain == '\0' ) {
+		return LDAP_PARAM_ERROR;
+	}
+
+    request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp."));
+    if (request == NULL) {
+		return LDAP_NO_MEMORY;
+    }
+    sprintf(request, "_ldap._tcp.%s", domain);
+
+#ifdef LDAP_R_COMPILE
+    ldap_pvt_thread_mutex_lock(&ldap_int_resolv_mutex);
+#endif
+
+    rc = LDAP_UNAVAILABLE;
+    len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
+    if (len >= 0) {
+	unsigned char *p;
+	char host[1024];
+	int status;
+	u_short port;
+	/* int priority, weight; */
+
+	/* Parse out query */
+	p = reply;
+	p += sizeof(HEADER);
+	status = dn_expand(reply, reply + len, p, host, sizeof(host));
+	if (status < 0) {
+	    goto out;
+	}
+	p += status;
+	p += 4;
+
+	while (p < reply + len) {
+	    int type, class, ttl, size;
+	    status = dn_expand(reply, reply + len, p, host, sizeof(host));
+	    if (status < 0) {
+		goto out;
+	    }
+	    p += status;
+	    type = (p[0] << 8) | p[1];
+	    p += 2;
+	    class = (p[0] << 8) | p[1];
+	    p += 2;
+	    ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+	    p += 4;
+	    size = (p[0] << 8) | p[1];
+	    p += 2;
+	    if (type == T_SRV) {
+		int buflen;
+		status = dn_expand(reply, reply + len, p + 6, host, sizeof(host));
+		if (status < 0) {
+		    goto out;
+		}
+		/* ignore priority and weight for now */
+		/* priority = (p[0] << 8) | p[1]; */
+		/* weight = (p[2] << 8) | p[3]; */
+		port = (p[4] << 8) | p[5];
+
+		buflen = strlen(host) + sizeof(":65355");
+		hostlist = (char *) LDAP_REALLOC(hostlist, cur + buflen);
+		if (hostlist == NULL) {
+		    rc = LDAP_NO_MEMORY;
+		    goto out;
+		}
+		if (cur > 0) {
+		    /* not first time around */
+		    hostlist[cur++] = ' ';
+		}
+		cur += sprintf(&hostlist[cur], "%s:%hd", host, port);
+	    }
+	    p += size;
+	}
+    }
+    if (hostlist == NULL) {
+	/* No LDAP servers found in DNS. */
+	rc = LDAP_UNAVAILABLE;
+	goto out;
+    }
+
+    rc = LDAP_SUCCESS;
+	*list = hostlist;
+
+  out:
+#ifdef LDAP_R_COMPILE
+    ldap_pvt_thread_mutex_unlock(&ldap_int_resolv_mutex);
+#endif
+
+    if (request != NULL) {
+	LDAP_FREE(request);
+    }
+    if (rc != LDAP_SUCCESS && hostlist != NULL) {
+	LDAP_FREE(hostlist);
+    }
+    return rc;
+#else
+    return LDAP_NOT_SUPPORTED;
+#endif /* HAVE_RES_QUERY */
+}
diff --git a/libraries/libldap/error.c b/libraries/libldap/error.c
index 8fc685b25f..3569887747 100644
--- a/libraries/libldap/error.c
+++ b/libraries/libldap/error.c
@@ -172,11 +172,6 @@ ldap_perror( LDAP *ld, LDAP_CONST char *str )
 	assert( LDAP_VALID( ld ) );
 	assert( str );
 
-	if ( ld == NULL ) {
-		fprintf( stderr, "ldap_perror: invalid session handle\n" );
-		return;
-	}
-
 	e = ldap_int_error( ld->ld_errno );
 
 	fprintf( stderr, "%s: %s (%d)\n",
@@ -257,10 +252,6 @@ ldap_parse_result(
 	assert( LDAP_VALID( ld ) );
 	assert( r != NULL );
 
-	if ( ld == NULL || r == NULL ) {
-		return LDAP_PARAM_ERROR;
-	}
-
 	if(errcodep != NULL) *errcodep = LDAP_SUCCESS;
 	if(matcheddnp != NULL) *matcheddnp = NULL;
 	if(errmsgp != NULL) *errmsgp = NULL;
diff --git a/libraries/libldap/extended.c b/libraries/libldap/extended.c
index 58bffe8620..74e31281ca 100644
--- a/libraries/libldap/extended.c
+++ b/libraries/libldap/extended.c
@@ -60,11 +60,6 @@ ldap_extended_operation(
 		return( ld->ld_errno );
 	}
 
-	if( reqoid == NULL || *reqoid == '\0' || msgidp == NULL ) {
-		ld->ld_errno = LDAP_PARAM_ERROR;
-		return( ld->ld_errno );
-	}
-
 	/* create a message to send */
 	if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
 		ld->ld_errno = LDAP_NO_MEMORY;
@@ -132,11 +127,6 @@ ldap_extended_operation_s(
 	assert( reqoid != NULL || *reqoid == '\0' );
 	assert( retoidp != NULL || retdatap != NULL );
 
-	if( retoidp == NULL || retdatap == NULL ) {
-		ld->ld_errno = LDAP_PARAM_ERROR;
-		return( ld->ld_errno );
-	}
-
     rc = ldap_extended_operation( ld, reqoid, reqdata,
 		sctrls, cctrls, &msgid );
         
diff --git a/libraries/libldap/getdn.c b/libraries/libldap/getdn.c
index bec27bc7da..4dd2279ee5 100644
--- a/libraries/libldap/getdn.c
+++ b/libraries/libldap/getdn.c
@@ -90,10 +90,9 @@ ldap_get_dn( LDAP *ld, LDAPMessage *entry )
 	Debug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 );
 #endif
 
-	if ( entry == NULL ) {
-		ld->ld_errno = LDAP_PARAM_ERROR;
-		return( NULL );
-	}
+	assert( ld != NULL );
+	assert( LDAP_VALID(ld) );
+	assert( entry != NULL );
 
 	tmp = *entry->lm_ber;	/* struct copy */
 	if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) {
@@ -2878,7 +2877,7 @@ ldap_rdn2bv( LDAPRDN *rdn, struct berval *bv, unsigned flags )
 		break;
 
 	default:
-		return( LDAP_PARAM_ERROR );
+		return LDAP_PARAM_ERROR;
 	}
 
 	bv->bv_val = LDAP_MALLOC( l + 1 );
diff --git a/libraries/libldap/getentry.c b/libraries/libldap/getentry.c
index f3f896e7b8..b75a4403f4 100644
--- a/libraries/libldap/getentry.c
+++ b/libraries/libldap/getentry.c
@@ -1,60 +1,130 @@
+/* $OpenLDAP$ */
 /*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/*  Portions
  *  Copyright (c) 1990 Regents of the University of Michigan.
  *  All rights reserved.
  *
  *  getentry.c
  */
 
-#ifndef lint 
-static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
-#endif
+#include "portable.h"
 
 #include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#ifdef MACOS
-#include <stdlib.h>
-#include "macos.h"
-#else /* MACOS */
-#if defined( DOS ) || defined( _WIN32 )
-#include <malloc.h>
-#include "msdos.h"
-#else /* DOS */
-#include <sys/types.h>
-#include <sys/socket.h>
-#endif /* DOS */
-#endif /* MACOS */
-
-#include "lber.h"
-#include "ldap.h"
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
 
 /* ARGSUSED */
 LDAPMessage *
 ldap_first_entry( LDAP *ld, LDAPMessage *chain )
 {
-	return( chain == NULLMSG || chain->lm_msgtype == LDAP_RES_SEARCH_RESULT
-	    ? NULLMSG : chain );
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+	assert( chain != NULL );
+
+	if( ld == NULL || chain == NULL ) {
+		return NULL;
+	}
+
+	return chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY
+		? chain
+		: ldap_next_entry( ld, chain );
 }
 
-/* ARGSUSED */
-LDAPMessage *ldap_next_entry( LDAP *ld, LDAPMessage *entry )
+LDAPMessage *
+ldap_next_entry( LDAP *ld, LDAPMessage *entry )
 {
-	if ( entry == NULLMSG || entry->lm_chain == NULLMSG
-	    || entry->lm_chain->lm_msgtype == LDAP_RES_SEARCH_RESULT )
-		return( NULLMSG );
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+	assert( entry != NULL );
 
-	return( entry->lm_chain );
+	if ( ld == NULL || entry == NULL ) {
+		return NULL;
+	}
+
+	for (
+		entry = entry->lm_chain;
+		entry != NULL;
+		entry = entry->lm_chain )
+	{
+		if( entry->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) {
+			return( entry );
+		}
+	}
+
+	return( NULL );
 }
 
-/* ARGSUSED */
 int
 ldap_count_entries( LDAP *ld, LDAPMessage *chain )
 {
 	int	i;
 
-	for ( i = 0; chain != NULL && chain->lm_msgtype
-	    != LDAP_RES_SEARCH_RESULT; chain = chain->lm_chain )
-		i++;
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+
+	if ( ld == NULL ) {
+		return -1;
+	}
+
+	for ( i = 0; chain != NULL; chain = chain->lm_chain ) {
+		if( chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) {
+			i++;
+		}
+	}
 
 	return( i );
 }
+
+int
+ldap_get_entry_controls(
+	LDAP *ld,
+	LDAPMessage *entry, 
+	LDAPControl ***sctrls )
+{
+	int rc;
+	BerElement be;
+
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+	assert( entry != NULL );
+	assert( sctrls != NULL );
+
+	if ( entry->lm_msgtype != LDAP_RES_SEARCH_ENTRY ) {
+		return LDAP_PARAM_ERROR;
+	}
+
+	/* make a local copy of the BerElement */
+	AC_MEMCPY(&be, entry->lm_ber, sizeof(be));
+
+	if ( ber_scanf( &be, "{xx" /*}*/ ) == LBER_ERROR ) {
+		rc = LDAP_DECODING_ERROR;
+		goto cleanup_and_return;
+	}
+
+	rc = ldap_int_get_controls( &be, sctrls );
+
+cleanup_and_return:
+	if( rc != LDAP_SUCCESS ) {
+		ld->ld_errno = rc;
+
+		if( ld->ld_matched != NULL ) {
+			LDAP_FREE( ld->ld_matched );
+			ld->ld_matched = NULL;
+		}
+
+		if( ld->ld_error != NULL ) {
+			LDAP_FREE( ld->ld_error );
+			ld->ld_error = NULL;
+		}
+	}
+
+	return rc;
+}
diff --git a/libraries/libldap/references.c b/libraries/libldap/references.c
new file mode 100644
index 0000000000..2aae3f469c
--- /dev/null
+++ b/libraries/libldap/references.c
@@ -0,0 +1,146 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/*
+ *  references.c
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+LDAPMessage *
+ldap_first_reference( LDAP *ld, LDAPMessage *chain )
+{
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+	assert( chain !=  NULL );
+
+	return chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE
+		? chain
+		: ldap_next_reference( ld, chain );
+}
+
+LDAPMessage *
+ldap_next_reference( LDAP *ld, LDAPMessage *ref )
+{
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+	assert( ref !=  NULL );
+
+	for (
+		ref = ref->lm_chain;
+		ref != NULL;
+		ref = ref->lm_chain )
+	{
+		if( ref->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) {
+			return( ref );
+		}
+	}
+
+	return( NULL );
+}
+
+int
+ldap_count_references( LDAP *ld, LDAPMessage *chain )
+{
+	int	i;
+
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+	assert( chain !=  NULL );
+
+	if ( ld == NULL ) {
+		return -1;
+	}
+
+	
+	for ( i = 0; chain != NULL; chain = chain->lm_chain ) {
+		if( chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) {
+			i++;
+		}
+	}
+
+	return( i );
+}
+
+int
+ldap_parse_reference( 
+	LDAP            *ld,    
+	LDAPMessage     *ref,
+	char            ***referralsp,
+	LDAPControl     ***serverctrls,
+	int             freeit)
+{
+	BerElement be;
+	char **refs = NULL;
+	int rc;
+
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+	assert( ref !=  NULL );
+
+	if( ref->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
+		return LDAP_PARAM_ERROR;
+	}
+
+	/* make a private copy of BerElement */
+	AC_MEMCPY(&be, ref->lm_ber, sizeof(be));
+	
+	if ( ber_scanf( &be, "{v" /*}*/, &refs ) == LBER_ERROR ) {
+		rc = LDAP_DECODING_ERROR;
+		goto free_and_return;
+	}
+
+	if ( serverctrls == NULL ) {
+		rc = LDAP_SUCCESS;
+		goto free_and_return;
+	}
+
+	if ( ber_scanf( &be, /*{*/ "}" ) == LBER_ERROR ) {
+		rc = LDAP_DECODING_ERROR;
+		goto free_and_return;
+	}
+
+	rc = ldap_int_get_controls( &be, serverctrls );
+
+free_and_return:
+
+	if( referralsp != NULL ) {
+		/* provide references regradless of return code */
+		*referralsp = refs;
+
+	} else {
+		LDAP_VFREE( refs );
+	}
+
+	if( freeit ) {
+		ldap_msgfree( ref );
+	}
+
+	if( rc != LDAP_SUCCESS ) {
+		ld->ld_errno = rc;
+
+		if( ld->ld_matched != NULL ) {
+			LDAP_FREE( ld->ld_matched );
+			ld->ld_matched = NULL;
+		}
+
+		if( ld->ld_error != NULL ) {
+			LDAP_FREE( ld->ld_error );
+			ld->ld_error = NULL;
+		}
+	}
+
+	return rc;
+}
diff --git a/libraries/libldap/result.c b/libraries/libldap/result.c
index f409afcc9e..a1ee317cf6 100644
--- a/libraries/libldap/result.c
+++ b/libraries/libldap/result.c
@@ -103,15 +103,6 @@ ldap_result(
 	Debug( LDAP_DEBUG_TRACE, "ldap_result msgid %d\n", msgid, 0, 0 );
 #endif
 
-	if( ld == NULL ) {
-		return -1;
-	}
-
-	if( result == NULL ) {
-		ld->ld_errno = LDAP_PARAM_ERROR;
-		return -1;
-	}
-
     lm = chkResponseList(ld, msgid, all);
 
 	if ( lm == NULL ) {
diff --git a/libraries/libldap/sasl.c b/libraries/libldap/sasl.c
index 93a52472f8..0164bc962f 100644
--- a/libraries/libldap/sasl.c
+++ b/libraries/libldap/sasl.c
@@ -75,11 +75,6 @@ ldap_sasl_bind(
 	rc = ldap_int_client_controls( ld, cctrls );
 	if( rc != LDAP_SUCCESS ) return rc;
 
-	if( msgidp == NULL ) {
-		ld->ld_errno = LDAP_PARAM_ERROR;
-		return ld->ld_errno;
-	}
-
 	if( mechanism == LDAP_SASL_SIMPLE ) {
 		if( dn == NULL && cred != NULL ) {
 			/* use default binddn */
@@ -269,10 +264,6 @@ ldap_parse_sasl_bind_result(
 	assert( LDAP_VALID( ld ) );
 	assert( res != NULL );
 
-	if ( ld == NULL || res == NULL ) {
-		return LDAP_PARAM_ERROR;
-	}
-
 	if( servercredp != NULL ) {
 		if( ld->ld_version < LDAP_VERSION2 ) {
 			return LDAP_NOT_SUPPORTED;
diff --git a/libraries/libldap/sort.c b/libraries/libldap/sort.c
index ee5d549c89..84557eb33a 100644
--- a/libraries/libldap/sort.c
+++ b/libraries/libldap/sort.c
@@ -1,4 +1,9 @@
+/* $OpenLDAP$ */
 /*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/* Portions
  * Copyright (c) 1994 Regents of the University of Michigan.
  * All rights reserved.
  *
@@ -12,53 +17,45 @@
  * sort.c:  LDAP library entry and value sort routines
  */
 
+#include "portable.h"
+
 #include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#ifdef MACOS
-#include "macos.h"
-#else /* MACOS */
-#ifdef DOS
-#include <malloc.h>
-#include "msdos.h"
-#endif /* DOS */
-#endif /* MACOS */
-
-#include "lber.h"
-#include "ldap.h"
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+
+#include "ldap-int.h"
 
 struct entrything {
 	char		**et_vals;
 	LDAPMessage	*et_msg;
+	int 		(*et_cmp_fn) LDAP_P((const char *a, const char *b));
 };
 
-#ifndef NEEDPROTOS
-static int	(*et_cmp_fn)();
-static int	et_cmp();
-#else /* !NEEDPROTOS */
-static int	(*et_cmp_fn)( char *a, char *b );
-static int	et_cmp( void *aa, void *bb);
-#endif /* !NEEDPROTOS */
+static int	et_cmp LDAP_P(( const void *aa, const void *bb));
+
 
 int
 ldap_sort_strcasecmp(
-    char	**a,
-    char	**b
+	LDAP_CONST void	*a,
+	LDAP_CONST void	*b
 )
 {
-	return( strcasecmp( *a, *b ) );
+	return( strcasecmp( *(char *const *)a, *(char *const *)b ) );
 }
 
 static int
 et_cmp(
-	void	*aa,
-	void	*bb
+	const void	*aa,
+	const void	*bb
 )
 {
 	int			i, rc;
-	struct entrything	*a = (struct entrything *)aa;
-	struct entrything	*b = (struct entrything *)bb;
+	const struct entrything	*a = (const struct entrything *)aa;
+	const struct entrything	*b = (const struct entrything *)bb;
 
 	if ( a->et_vals == NULL && b->et_vals == NULL )
 		return( 0 );
@@ -68,8 +65,7 @@ et_cmp(
 		return( 1 );
 
 	for ( i = 0; a->et_vals[i] && b->et_vals[i]; i++ ) {
-		if ( (rc = (*et_cmp_fn)( a->et_vals[i], b->et_vals[i] ))
-		    != 0 ) {
+		if ( (rc = a->et_cmp_fn( a->et_vals[i], b->et_vals[i] )) != 0 ) {
 			return( rc );
 		}
 	}
@@ -85,8 +81,8 @@ int
 ldap_sort_entries(
     LDAP	*ld,
     LDAPMessage	**chain,
-    char	*attr,		/* NULL => sort by DN */
-    int		(*cmp)()
+    LDAP_CONST char	*attr,		/* NULL => sort by DN */
+    int		(*cmp) (LDAP_CONST  char *, LDAP_CONST char *)
 )
 {
 	int			i, count;
@@ -94,9 +90,19 @@ ldap_sort_entries(
 	LDAPMessage		*e, *last;
 	LDAPMessage		**ep;
 
+	assert( ld != NULL );
+
 	count = ldap_count_entries( ld, *chain );
 
-	if ( (et = (struct entrything *) malloc( count *
+	if ( count < 0 ) {
+		return -1;
+
+	} else if ( count < 2 ) {
+		/* zero or one entries -- already sorted! */
+		return 0;
+	}
+
+	if ( (et = (struct entrything *) LDAP_MALLOC( count *
 	    sizeof(struct entrything) )) == NULL ) {
 		ld->ld_errno = LDAP_NO_MEMORY;
 		return( -1 );
@@ -104,13 +110,14 @@ ldap_sort_entries(
 
 	e = *chain;
 	for ( i = 0; i < count; i++ ) {
+		et[i].et_cmp_fn = cmp;
 		et[i].et_msg = e;
 		if ( attr == NULL ) {
 			char	*dn;
 
 			dn = ldap_get_dn( ld, e );
 			et[i].et_vals = ldap_explode_dn( dn, 1 );
-			free( dn );
+			LDAP_FREE( dn );
 		} else {
 			et[i].et_vals = ldap_get_values( ld, e, attr );
 		}
@@ -119,18 +126,17 @@ ldap_sort_entries(
 	}
 	last = e;
 
-	et_cmp_fn = cmp;
-	qsort( et, count, sizeof(struct entrything), (void *) et_cmp );
+	qsort( et, count, sizeof(struct entrything), et_cmp );
 
 	ep = chain;
 	for ( i = 0; i < count; i++ ) {
 		*ep = et[i].et_msg;
 		ep = &(*ep)->lm_chain;
 
-		ldap_value_free( et[i].et_vals );
+		LDAP_VFREE( et[i].et_vals );
 	}
 	*ep = last;
-	free( (char *) et );
+	LDAP_FREE( (char *) et );
 
 	return( 0 );
 }
@@ -139,7 +145,7 @@ int
 ldap_sort_values(
     LDAP	*ld,
     char	**vals,
-    int		(*cmp)()
+    int		(*cmp) (LDAP_CONST void *, LDAP_CONST void *)
 )
 {
 	int	nel;
diff --git a/libraries/libldap/sortctrl.c b/libraries/libldap/sortctrl.c
new file mode 100644
index 0000000000..f215a839d2
--- /dev/null
+++ b/libraries/libldap/sortctrl.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/* Adapted for inclusion into OpenLDAP by Kurt D. Zeilenga */
+/*---
+ * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *
+ *---*/
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#define LDAP_MATCHRULE_IDENTIFIER      0x80L
+#define LDAP_REVERSEORDER_IDENTIFIER   0x81L
+#define LDAP_ATTRTYPES_IDENTIFIER      0x80L
+
+
+
+/* ---------------------------------------------------------------------------
+   countKeys
+   
+   Internal function to determine the number of keys in the string.
+   
+   keyString  (IN) String of items separated by whitespace.
+   ---------------------------------------------------------------------------*/
+
+static int countKeys(char *keyString)
+{
+	char *p = keyString;
+	int count = 0;
+
+	for (;;)
+	{
+		while (LDAP_SPACE(*p))		 /* Skip leading whitespace */
+			p++;
+
+		if (*p == '\0')			/* End of string? */
+			return count;
+
+		count++;				/* Found start of a key */
+
+		while (!LDAP_SPACE(*p))	/* Skip till next space or end of string. */
+			if (*p++ == '\0')
+				return count;
+	}
+}
+
+
+/* ---------------------------------------------------------------------------
+   readNextKey
+   
+   Internal function to parse the next sort key in the string.
+   Allocate an LDAPSortKey structure and initialize it with
+   attribute name, reverse flag, and matching rule OID.
+
+   Each sort key in the string has the format:
+	  [whitespace][-]attribute[:[OID]]
+
+   pNextKey    (IN/OUT) Points to the next key in the sortkey string to parse.
+						The pointer is updated to point to the next character
+						after the sortkey being parsed.
+						
+   key         (OUT)    Points to the address of an LDAPSortKey stucture
+						which has been allocated by this routine and
+						initialized with information from the next sortkey.                        
+   ---------------------------------------------------------------------------*/
+
+static int readNextKey( char **pNextKey, LDAPSortKey **key)
+{
+	char *p = *pNextKey;
+	int rev = 0;
+	char *attrStart;
+	int attrLen;
+	char *oidStart = NULL;
+	int oidLen = 0;
+
+	/* Skip leading white space. */
+	while (LDAP_SPACE(*p))
+		p++;
+
+	if (*p == '-')		 /* Check if the reverse flag is present. */
+	{
+		rev=1;
+		p++;
+	}
+
+	/* We're now positioned at the start of the attribute. */
+	attrStart = p;
+
+	/* Get the length of the attribute until the next whitespace or ":". */
+	attrLen = strcspn(p, " \t:");
+	p += attrLen;
+
+	if (attrLen == 0)	 /* If no attribute name was present, quit. */
+		return LDAP_PARAM_ERROR;
+
+	if (*p == ':')
+	{
+		oidStart = ++p;				 /* Start of the OID, after the colon */
+		oidLen = strcspn(p, " \t");	 /* Get length of OID till next whitespace */
+		p += oidLen;
+	}
+
+	*pNextKey = p;		 /* Update argument to point to next key */
+
+	/* Allocate an LDAPSortKey structure */
+	*key = LDAP_MALLOC(sizeof(LDAPSortKey));
+	if (*key == NULL) return LDAP_NO_MEMORY;
+
+	/* Allocate memory for the attribute and copy to it. */
+	(*key)->attributeType = LDAP_MALLOC(attrLen+1);
+	if ((*key)->attributeType == NULL) {
+		LDAP_FREE(*key);
+		return LDAP_NO_MEMORY;
+	}
+
+	strncpy((*key)->attributeType, attrStart, attrLen);
+	(*key)->attributeType[attrLen] = 0;
+
+	/* If present, allocate memory for the OID and copy to it. */
+	if (oidLen) {
+		(*key)->orderingRule = LDAP_MALLOC(oidLen+1);
+		if ((*key)->orderingRule == NULL) {
+			LDAP_FREE((*key)->attributeType);
+			LDAP_FREE(*key);
+			return LDAP_NO_MEMORY;
+		}
+		strncpy((*key)->orderingRule, oidStart, oidLen);
+		(*key)->orderingRule[oidLen] = 0;
+
+	} else {
+		(*key)->orderingRule = NULL;
+	}
+
+	(*key)->reverseOrder = rev;
+
+	return LDAP_SUCCESS;
+}
+
+
+/* ---------------------------------------------------------------------------
+   ldap_create_sort_keylist
+   
+   Create an array of pointers to LDAPSortKey structures, containing the
+   information specified by the string representation of one or more
+   sort keys.
+   
+   sortKeyList    (OUT) Points to a null-terminated array of pointers to
+						LDAPSortKey structures allocated by this routine.
+						This memory SHOULD be freed by the calling program
+						using ldap_free_sort_keylist().
+						
+   keyString      (IN)  Points to a string of one or more sort keys.                      
+   
+   ---------------------------------------------------------------------------*/
+
+int
+ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString )
+{
+	int         numKeys, rc, i;
+	char        *nextKey;
+	LDAPSortKey **keyList = NULL;
+
+	assert( sortKeyList != NULL );
+	assert( keyString != NULL );
+
+	*sortKeyList = NULL;
+
+	/* Determine the number of sort keys so we can allocate memory. */
+	if (( numKeys = countKeys(keyString)) == 0) {
+		return LDAP_PARAM_ERROR;
+	}
+
+	/* Allocate the array of pointers.  Initialize to NULL. */
+	keyList=(LDAPSortKey**)LBER_CALLOC(numKeys+1, sizeof(LDAPSortKey*));
+	if ( keyList == NULL) return LDAP_NO_MEMORY;
+
+	/* For each sort key in the string, create an LDAPSortKey structure
+	   and add it to the list.
+	*/
+	nextKey = keyString;		  /* Points to the next key in the string */
+	for (i=0; i < numKeys; i++) {
+		rc = readNextKey(&nextKey, &keyList[i]);
+
+		if (rc != LDAP_SUCCESS) {
+			ldap_free_sort_keylist(keyList);
+			return rc;
+		}
+	}
+
+	*sortKeyList = keyList;
+	return LDAP_SUCCESS;
+}
+
+
+/* ---------------------------------------------------------------------------
+   ldap_free_sort_keylist
+   
+   Frees the sort key structures created by ldap_create_sort_keylist().
+   Frees the memory referenced by the LDAPSortKey structures,
+   the LDAPSortKey structures themselves, and the array of pointers
+   to the structures.
+   
+   keyList     (IN) Points to an array of pointers to LDAPSortKey structures.
+   ---------------------------------------------------------------------------*/
+
+void
+ldap_free_sort_keylist ( LDAPSortKey **keyList )
+{
+	int i;
+	LDAPSortKey *nextKeyp;
+
+	if (keyList == NULL) return;
+
+	i=0;
+	while ( 0 != (nextKeyp = keyList[i++]) ) {
+		if (nextKeyp->attributeType) {
+			LBER_FREE(nextKeyp->attributeType);
+		}
+
+		if (nextKeyp->orderingRule != NULL) {
+			LBER_FREE(nextKeyp->orderingRule);
+		}
+
+		LBER_FREE(nextKeyp);
+	}
+
+	LBER_FREE(keyList);
+}
+
+
+/* ---------------------------------------------------------------------------
+   ldap_create_sort_control
+   
+   Create and encode the server-side sort control.
+   
+   ld          (IN) An LDAP session handle, as obtained from a call to
+					ldap_init().
+
+   keyList     (IN) Points to a null-terminated array of pointers to
+					LDAPSortKey structures, containing a description of
+					each of the sort keys to be used.  The description
+					consists of an attribute name, ascending/descending flag,
+					and an optional matching rule (OID) to use.
+			   
+   isCritical  (IN) 0 - Indicates the control is not critical to the operation.
+					non-zero - The control is critical to the operation.
+					 
+   ctrlp      (OUT) Returns a pointer to the LDAPControl created.  This control
+					SHOULD be freed by calling ldap_control_free() when done.
+   
+   
+   Ber encoding
+   
+   SortKeyList ::= SEQUENCE OF SEQUENCE {
+		   attributeType   AttributeDescription,
+		   orderingRule    [0] MatchingRuleId OPTIONAL,
+		   reverseOrder    [1] BOOLEAN DEFAULT FALSE }
+   
+   ---------------------------------------------------------------------------*/
+
+int
+ldap_create_sort_control (
+	LDAP *ld,
+	LDAPSortKey **keyList,
+	int isCritical,
+	LDAPControl **ctrlp )
+{
+	int         i;
+	BerElement  *ber;
+	ber_tag_t tag;
+
+
+	if ( (ld == NULL) || (keyList == NULL) || (ctrlp == NULL) ) {
+		ld->ld_errno = LDAP_PARAM_ERROR;
+		return(ld->ld_errno);
+	}
+
+	if ((ber = ldap_alloc_ber_with_options(ld)) == NULL) {
+		ld->ld_errno = LDAP_NO_MEMORY;
+		return( ld->ld_errno );
+	}
+
+	tag = ber_printf(ber, "{" /*}*/);
+	if (tag == LBER_ERROR) goto exit;
+
+	for (i = 0; keyList[i] != NULL; i++) {
+		tag = ber_printf(ber, "{s" /*}*/, (keyList[i])->attributeType);
+		if (tag == LBER_ERROR) goto exit;
+
+		if ((keyList[i])->orderingRule != NULL) {
+			tag = ber_printf( ber, "ts",
+				LDAP_MATCHRULE_IDENTIFIER,
+				(keyList[i])->orderingRule );
+
+			if( tag == LBER_ERROR ) goto exit;
+		}
+
+		if ((keyList[i])->reverseOrder) {
+			tag = ber_printf(ber, "tb",
+				LDAP_REVERSEORDER_IDENTIFIER,
+				(keyList[i])->reverseOrder );
+
+			if( tag == LBER_ERROR ) goto exit;
+		}
+
+		tag = ber_printf(ber, /*{*/ "N}");
+		if( tag == LBER_ERROR ) goto exit;
+	}
+
+	tag = ber_printf(ber, /*{*/ "N}");
+	if( tag == LBER_ERROR ) goto exit;
+
+	ld->ld_errno = ldap_create_control( LDAP_CONTROL_SORTREQUEST,
+		ber, isCritical, ctrlp);
+
+	ber_free(ber, 1);
+
+	return(ld->ld_errno);
+
+exit:
+	ber_free(ber, 1);
+	ld->ld_errno =  LDAP_ENCODING_ERROR;
+	return(ld->ld_errno);
+}
+
+
+/* ---------------------------------------------------------------------------
+   ldap_parse_sort_control
+   
+   Decode the server-side sort control return information.
+
+   ld          (IN) An LDAP session handle, as obtained from a call to
+					ldap_init().
+
+   ctrls       (IN) The address of a NULL-terminated array of LDAPControl
+					structures, typically obtained by a call to
+					ldap_parse_result().
+				  
+   returnCode (OUT) This result parameter is filled in with the sort control
+					result code.  This parameter MUST not be NULL.
+				  
+   attribute  (OUT) If an error occured the server may return a string
+					indicating the first attribute in the sortkey list
+					that was in error.  If a string is returned, the memory
+					should be freed with ldap_memfree.  If this parameter is
+					NULL, no string is returned.
+   
+			   
+   Ber encoding for sort control
+	 
+	 SortResult ::= SEQUENCE {
+		sortResult  ENUMERATED {
+			success                   (0), -- results are sorted
+			operationsError           (1), -- server internal failure
+			timeLimitExceeded         (3), -- timelimit reached before
+										   -- sorting was completed
+			strongAuthRequired        (8), -- refused to return sorted
+										   -- results via insecure
+										   -- protocol
+			adminLimitExceeded       (11), -- too many matching entries
+										   -- for the server to sort
+			noSuchAttribute          (16), -- unrecognized attribute
+										   -- type in sort key
+			inappropriateMatching    (18), -- unrecognized or inappro-
+										   -- priate matching rule in
+										   -- sort key
+			insufficientAccessRights (50), -- refused to return sorted
+										   -- results to this client
+			busy                     (51), -- too busy to process
+			unwillingToPerform       (53), -- unable to sort
+			other                    (80)
+			},
+	  attributeType [0] AttributeDescription OPTIONAL }
+   ---------------------------------------------------------------------------*/
+
+int
+ldap_parse_sort_control(
+	LDAP           *ld,
+	LDAPControl    **ctrls,
+	unsigned long  *returnCode,
+	char           **attribute )
+{
+	BerElement *ber;
+	LDAPControl *pControl;
+	int i;
+	ber_tag_t tag, berTag;
+	ber_len_t berLen;
+
+	if (ld == NULL) {
+		ld->ld_errno = LDAP_PARAM_ERROR;
+		return(ld->ld_errno);
+	}
+
+	if (ctrls == NULL) {
+		ld->ld_errno =  LDAP_CONTROL_NOT_FOUND;
+		return(ld->ld_errno);
+	}
+
+	if (attribute) {
+		*attribute = NULL;
+	}
+
+	/* Search the list of control responses for a sort control. */
+	for (i=0; ctrls[i]; i++) {
+		pControl = ctrls[i];
+		if (!strcmp(LDAP_CONTROL_SORTRESPONSE, pControl->ldctl_oid))
+			goto foundSortControl;
+	}
+
+	/* No sort control was found. */
+	ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+	return(ld->ld_errno);
+
+foundSortControl:
+	/* Create a BerElement from the berval returned in the control. */
+	ber = ber_init(&pControl->ldctl_value);
+
+	if (ber == NULL) {
+		ld->ld_errno = LDAP_NO_MEMORY;
+		return(ld->ld_errno);
+	}
+
+	/* Extract the result code from the control. */
+	tag = ber_scanf(ber, "{e" /*}*/, returnCode);
+
+	if( tag == LBER_ERROR ) {
+		ber_free(ber, 1);
+		ld->ld_errno = LDAP_DECODING_ERROR;
+		return(ld->ld_errno);
+	}
+
+	/* If caller wants the attribute name, and if it's present in the control,
+	   extract the attribute name which caused the error. */
+	if (attribute && (LDAP_ATTRTYPES_IDENTIFIER == ber_peek_tag(ber, &berLen)))
+	{
+		tag = ber_scanf(ber, "ta", &berTag, attribute);
+
+		if (tag == LBER_ERROR ) {
+			ber_free(ber, 1);
+			ld->ld_errno = LDAP_DECODING_ERROR;
+			return(ld->ld_errno);
+		}
+	}
+
+	ber_free(ber,1);
+
+	ld->ld_errno = LDAP_SUCCESS;
+	return(ld->ld_errno);
+}
diff --git a/libraries/libldap/url.c b/libraries/libldap/url.c
index 97059170b7..e7985dae6b 100644
--- a/libraries/libldap/url.c
+++ b/libraries/libldap/url.c
@@ -886,10 +886,10 @@ ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
 	LDAPURLDesc *ludp;
 	char **urls;
 
-	*ludlist = NULL;
+	assert( ludlist != NULL );
+	assert( url != NULL );
 
-	if (url == NULL)
-		return LDAP_PARAM_ERROR;
+	*ludlist = NULL;
 
 	urls = ldap_str2charray(url, ", ");
 	if (urls == NULL)
@@ -923,10 +923,10 @@ ldap_url_parsehosts(
 	LDAPURLDesc *ludp;
 	char **specs, *p;
 
-	*ludlist = NULL;
+	assert( ludlist != NULL );
+	assert( hosts != NULL );
 
-	if (hosts == NULL)
-		return LDAP_PARAM_ERROR;
+	*ludlist = NULL;
 
 	specs = ldap_str2charray(hosts, ", ");
 	if (specs == NULL)
diff --git a/libraries/libldap/vlvctrl.c b/libraries/libldap/vlvctrl.c
new file mode 100644
index 0000000000..0ee5ed2ab4
--- /dev/null
+++ b/libraries/libldap/vlvctrl.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/* Adapted for inclusion into OpenLDAP by Kurt D. Zeilenga */
+/*---
+ * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---*/
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#define LDAP_VLVBYINDEX_IDENTIFIER     0xa0L
+#define LDAP_VLVBYVALUE_IDENTIFIER     0x81L
+#define LDAP_VLVCONTEXT_IDENTIFIER     0x04L
+
+
+/*---
+   ldap_create_vlv_control
+   
+   Create and encode the Virtual List View control.
+
+   ld        (IN)  An LDAP session handle, as obtained from a call to
+				   ldap_init().
+   
+   vlvinfop  (IN)  The address of an LDAPVLVInfo structure whose contents 
+				   are used to construct the value of the control
+				   that is created.
+   
+   ctrlp     (OUT) A result parameter that will be assigned the address
+				   of an LDAPControl structure that contains the 
+				   VirtualListViewRequest control created by this function.
+				   The memory occupied by the LDAPControl structure
+				   SHOULD be freed when it is no longer in use by
+				   calling ldap_control_free().
+					  
+   
+   Ber encoding
+   
+   VirtualListViewRequest ::= SEQUENCE {
+		beforeCount  INTEGER (0 .. maxInt),
+		afterCount   INTEGER (0 .. maxInt),
+		CHOICE {
+				byoffset [0] SEQUENCE, {
+				offset        INTEGER (0 .. maxInt),
+				contentCount  INTEGER (0 .. maxInt) }
+				[1] greaterThanOrEqual assertionValue }
+		contextID     OCTET STRING OPTIONAL }
+	  
+   
+   Note:  The first time the VLV control is created, the ldvlv_context
+		  field of the LDAPVLVInfo structure should be set to NULL.
+		  The context obtained from calling ldap_parse_vlv_control()
+		  should be used as the context in the next ldap_create_vlv_control
+		  call.
+
+ ---*/
+
+int
+ldap_create_vlv_control( LDAP *ld,
+						 LDAPVLVInfo *vlvinfop,
+						 LDAPControl **ctrlp )
+{
+	ber_tag_t tag;
+	BerElement *ber;
+
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+	assert( vlvinfop != NULL );
+	assert( ctrlp != NULL );
+
+	if ((ber = ldap_alloc_ber_with_options(ld)) == NULL) {
+		ld->ld_errno = LDAP_NO_MEMORY;
+		return(LDAP_NO_MEMORY);
+	}
+
+	tag = ber_printf(ber, "{ii" /*}*/,
+		vlvinfop->ldvlv_before_count,
+		vlvinfop->ldvlv_after_count);
+	if( tag == LBER_ERROR ) goto exit;
+
+	if (vlvinfop->ldvlv_attrvalue == NULL) {
+		tag = ber_printf(ber, "t{iiN}",
+			LDAP_VLVBYINDEX_IDENTIFIER,
+			vlvinfop->ldvlv_offset,
+			vlvinfop->ldvlv_count);
+		if( tag == LBER_ERROR ) goto exit;
+
+	} else {
+		tag = ber_printf(ber, "tO",
+			LDAP_VLVBYVALUE_IDENTIFIER,
+			vlvinfop->ldvlv_attrvalue);
+		if( tag == LBER_ERROR ) goto exit;
+	}
+
+	if (vlvinfop->ldvlv_context) {
+		tag = ber_printf(ber, "tO",
+			LDAP_VLVCONTEXT_IDENTIFIER,
+			vlvinfop->ldvlv_context);
+		if( tag == LBER_ERROR ) goto exit;
+	}
+
+	tag = ber_printf(ber, /*{*/ "N}"); 
+	if( tag == LBER_ERROR ) goto exit;
+
+	ld->ld_errno = ldap_create_control(	LDAP_CONTROL_VLVREQUEST,
+		ber, 1, ctrlp);
+
+	ber_free(ber, 1);
+	return(ld->ld_errno);
+
+exit:
+	ber_free(ber, 1);
+	ld->ld_errno = LDAP_ENCODING_ERROR;
+	return(ld->ld_errno);
+}
+
+
+/*---
+   ldap_parse_vlv_control
+   
+   Decode the Virtual List View control return information.
+
+   ld           (IN)   An LDAP session handle.
+   
+   ctrls        (IN)   The address of a NULL-terminated array of 
+					   LDAPControl structures, typically obtained 
+					   by a call to ldap_parse_result().
+   
+   target_posp	(OUT)  This result parameter is filled in with the list
+					   index of the target entry.  If this parameter is
+					   NULL, the target position is not returned.
+   
+   list_countp  (OUT)  This result parameter is filled in with the server's
+					   estimate of the size of the list.  If this parameter
+					   is NULL, the size is not returned.
+   
+   contextp     (OUT)  This result parameter is filled in with the address
+					   of a struct berval that contains the server-
+					   generated context identifier if one was returned by
+					   the server.  If the server did not return a context
+					   identifier, this parameter will be set to NULL, even
+					   if an error occured.
+					   The returned context SHOULD be used in the next call
+					   to create a VLV sort control.  The struct berval
+					   returned SHOULD be disposed of by calling ber_bvfree()
+					   when it is no longer needed.  If NULL is passed for
+					   contextp, the context identifier is not returned.
+   
+   errcodep     (OUT)  This result parameter is filled in with the VLV
+					   result code.  If this parameter is NULL, the result
+					   code is not returned.  
+   
+   
+   Ber encoding
+   
+   VirtualListViewResponse ::= SEQUENCE {
+		targetPosition    INTEGER (0 .. maxInt),
+		contentCount     INTEGER (0 .. maxInt),
+		virtualListViewResult ENUMERATED {
+		success (0),
+		operatonsError (1),
+		unwillingToPerform (53),
+		insufficientAccessRights (50),
+		busy (51),
+		timeLimitExceeded (3),
+		adminLimitExceeded (11),
+		sortControlMissing (60),
+		offsetRangeError (61),
+		other (80) },
+		contextID     OCTET STRING OPTIONAL }
+   
+---*/
+
+int
+ldap_parse_vlv_control(
+	LDAP           *ld,
+	LDAPControl    **ctrls,
+	unsigned long  *target_posp,
+	unsigned long  *list_countp,
+	struct berval  **contextp,
+	int            *errcodep )
+{
+	BerElement  *ber;
+	LDAPControl *pControl;
+	int i;
+	unsigned long pos, count, err;
+	ber_tag_t tag, berTag;
+	ber_len_t berLen;
+
+	assert( ld != NULL );
+	assert( LDAP_VALID( ld ) );
+
+	if (contextp) {
+		*contextp = NULL;	 /* Make sure we return a NULL if error occurs. */
+	}
+
+	if (ctrls == NULL) {
+		ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+		return(ld->ld_errno);
+	}
+
+	/* Search the list of control responses for a VLV control. */
+	for (i=0; ctrls[i]; i++) {
+		pControl = ctrls[i];
+		if (!strcmp(LDAP_CONTROL_VLVRESPONSE, pControl->ldctl_oid))
+			goto foundVLVControl;
+	}
+
+	/* No sort control was found. */
+	ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+	return(ld->ld_errno);
+
+foundVLVControl:
+	/* Create a BerElement from the berval returned in the control. */
+	ber = ber_init(&pControl->ldctl_value);
+
+	if (ber == NULL) {
+		ld->ld_errno = LDAP_NO_MEMORY;
+		return(ld->ld_errno);
+	}
+
+	/* Extract the data returned in the control. */
+	tag = ber_scanf(ber, "{iie" /*}*/, &pos, &count, &err);
+
+	if( tag == LBER_ERROR) {
+		ber_free(ber, 1);
+		ld->ld_errno = LDAP_DECODING_ERROR;
+		return(ld->ld_errno);
+	}
+
+
+	/* Since the context is the last item encoded, if caller doesn't want
+	   it returned, don't decode it. */
+	if (contextp) {
+		if (LDAP_VLVCONTEXT_IDENTIFIER == ber_peek_tag(ber, &berLen)) {
+			tag = ber_scanf(ber, "tO", &berTag, contextp);
+
+			if( tag == LBER_ERROR) {
+				ber_free(ber, 1);
+				ld->ld_errno = LDAP_DECODING_ERROR;
+				return(ld->ld_errno);
+			}
+		}
+	}
+
+	ber_free(ber, 1);
+
+	/* Return data to the caller for items that were requested. */
+	if (target_posp) {
+		*target_posp = pos;
+	}
+	if (list_countp) {
+		*list_countp = count;
+	}
+	if (errcodep) {
+		*errcodep = err;
+	}
+
+	ld->ld_errno = LDAP_SUCCESS;
+	return(ld->ld_errno);
+}
diff --git a/libraries/libldap_r/tpool.c b/libraries/libldap_r/tpool.c
index 923a1b9ce4..d48a8e9370 100644
--- a/libraries/libldap_r/tpool.c
+++ b/libraries/libldap_r/tpool.c
@@ -399,7 +399,11 @@ ldap_int_thread_pool_wrapper (
 		ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
 
 		(ctx->ltc_start_routine)(ctx->ltc_arg);
+
+		ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
 		LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, ctx, ltc_next.l);
+		ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+
 		ldap_pvt_thread_yield();
 
 		/* if we use an idle timer, here's
diff --git a/libraries/liblutil/entropy.c b/libraries/liblutil/entropy.c
index cd379c6724..74d3f3210a 100644
--- a/libraries/liblutil/entropy.c
+++ b/libraries/liblutil/entropy.c
@@ -36,21 +36,27 @@ int lutil_entropy( unsigned char *buf, ber_len_t nbytes )
 	if( nbytes == 0 ) return 0;
 
 #ifdef URANDOM_DEVICE
+#define URANDOM_NREADS 4
 	/* Linux and *BSD offer a urandom device */
 	{
-		int rc, fd;
+		int rc, fd, n=0;
 
 		fd = open( URANDOM_DEVICE, O_RDONLY );
 
 		if( fd < 0 ) return -1;
 
-		rc = read( fd, buf, nbytes );
-		close(fd);
+		do {
+			rc = read( fd, buf, nbytes );
+			if( rc <= 0 ) break;
 
-		/* should return nbytes */
-		if( rc != nbytes ) return -1;
+			buf+=rc;
+			nbytes-=rc;
 
-		return 0;
+			if( ++n >= URANDOM_NREADS ) break;
+		} while( nbytes > 0 );
+
+		close(fd);
+		return nbytes > 0 ? -1 : 0;
 	}
 #elif PROV_RSA_FULL
 	{
diff --git a/servers/slapd/at.c b/servers/slapd/at.c
index 91015007b9..ef73fe7b45 100644
--- a/servers/slapd/at.c
+++ b/servers/slapd/at.c
@@ -235,7 +235,7 @@ at_insert(
 				 (AVL_DUP) avl_dup_error ) ) {
 			*err = sat->sat_oid;
 			ldap_memfree(air);
-			return SLAP_SCHERR_DUP_ATTR;
+			return SLAP_SCHERR_ATTR_DUP;
 		}
 		/* FIX: temporal consistency check */
 		at_bvfind(&air->air_name);
@@ -253,7 +253,7 @@ at_insert(
 					 (AVL_DUP) avl_dup_error ) ) {
 				*err = *names;
 				ldap_memfree(air);
-				return SLAP_SCHERR_DUP_ATTR;
+				return SLAP_SCHERR_ATTR_DUP;
 			}
 			/* FIX: temporal consistency check */
 			at_bvfind(&air->air_name);
@@ -371,6 +371,11 @@ at_add(
 			/* subtypes must have same usage as their SUP */
 			return SLAP_SCHERR_ATTR_BAD_USAGE;
 		}
+
+		if ( sat->sat_flags & SLAP_AT_FINAL ) {
+			/* cannot subtype a "final" attribute type */
+			return SLAP_SCHERR_ATTR_BAD_SUP;
+		}
 	}
 
 	/*
@@ -467,6 +472,9 @@ at_schema_info( Entry *e )
 		if ( ldap_attributetype2bv( &at->sat_atype, vals ) == NULL ) {
 			return -1;
 		}
+
+		if( at->sat_flags & SLAP_AT_HIDE ) continue;
+
 #if 0
 		Debug( LDAP_DEBUG_TRACE, "Merging at [%ld] %s\n",
 		       (long) vals[0].bv_len, vals[0].bv_val, 0 );
diff --git a/servers/slapd/back-ldbm/modify.c b/servers/slapd/back-ldbm/modify.c
index 2dfb9be680..03f81b1687 100644
--- a/servers/slapd/back-ldbm/modify.c
+++ b/servers/slapd/back-ldbm/modify.c
@@ -110,7 +110,6 @@ int ldbm_modify_internal(
 #endif
 
 			rc = modify_replace_values( e, mod, text, textbuf, textlen );
-			assert( rc != LDAP_TYPE_OR_VALUE_EXISTS );
 			if( rc != LDAP_SUCCESS ) {
 #ifdef NEW_LOGGING
 				LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
diff --git a/servers/slapd/back-shell/abandon.c b/servers/slapd/back-shell/abandon.c
index cae990af5b..3f0f6ed28d 100644
--- a/servers/slapd/back-shell/abandon.c
+++ b/servers/slapd/back-shell/abandon.c
@@ -38,24 +38,11 @@ shell_back_abandon(
 				break;
 			}
 		}
-		if( pid == -1 ) {
-			LDAP_STAILQ_FOREACH( o, &conn->c_pending_ops, o_next ) {
-				if ( o->o_msgid == msgid ) {
-					pid = (pid_t) o->o_private;
-					break;
-				}
-			}
-		}
 		ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
+	}
 
-		if ( pid != -1 ) {
-			Debug( LDAP_DEBUG_ARGS, "shell killing pid %d\n",
-			       (int) pid, 0, 0 );
-			kill( pid, SIGTERM );
-		} else {
-			Debug( LDAP_DEBUG_ARGS, "shell could not find op %d\n",
-			    msgid, 0, 0 );
-		}
+	if ( pid == -1 ) {
+		Debug( LDAP_DEBUG_ARGS, "shell could not find op %d\n", msgid, 0, 0 );
 		return 0;
 	}
 
@@ -65,10 +52,9 @@ shell_back_abandon(
 
 	/* write out the request to the abandon process */
 	fprintf( wfp, "ABANDON\n" );
-	fprintf( wfp, "opid: %ld/%ld\n", op->o_connid, (long) op->o_msgid );
 	fprintf( wfp, "msgid: %d\n", msgid );
 	print_suffixes( wfp, be );
-	fprintf( wfp, "abandonid: %ld/%d\n", op->o_connid, msgid );
+	fprintf( wfp, "pid: %ld\n", (long) pid );
 	fclose( wfp );
 
 	/* no result from abandon */
diff --git a/servers/slapd/back-shell/add.c b/servers/slapd/back-shell/add.c
index 134416866b..1d08c0c088 100644
--- a/servers/slapd/back-shell/add.c
+++ b/servers/slapd/back-shell/add.c
@@ -41,7 +41,6 @@ shell_back_add(
 
 	/* write out the request to the add process */
 	fprintf( wfp, "ADD\n" );
-	fprintf( wfp, "opid: %ld/%ld\n", op->o_connid, (long) op->o_msgid );
 	fprintf( wfp, "msgid: %ld\n", (long) op->o_msgid );
 	print_suffixes( wfp, be );
 	ldap_pvt_thread_mutex_lock( &entry2str_mutex );
diff --git a/servers/slapd/back-shell/bind.c b/servers/slapd/back-shell/bind.c
index fe36059215..e41c06c1f2 100644
--- a/servers/slapd/back-shell/bind.c
+++ b/servers/slapd/back-shell/bind.c
@@ -46,7 +46,6 @@ shell_back_bind(
 
 	/* write out the request to the bind process */
 	fprintf( wfp, "BIND\n" );
-	fprintf( wfp, "opid: %ld/%ld\n", op->o_connid, (long) op->o_msgid );
 	fprintf( wfp, "msgid: %ld\n", (long) op->o_msgid );
 	print_suffixes( wfp, be );
 	fprintf( wfp, "dn: %s\n", dn->bv_val );
diff --git a/servers/slapd/back-shell/compare.c b/servers/slapd/back-shell/compare.c
index b5ef2d24eb..4b19e8f374 100644
--- a/servers/slapd/back-shell/compare.c
+++ b/servers/slapd/back-shell/compare.c
@@ -48,7 +48,6 @@ shell_back_compare(
 
 	/* write out the request to the compare process */
 	fprintf( wfp, "COMPARE\n" );
-	fprintf( wfp, "opid: %ld/%ld\n", op->o_connid, (long) op->o_msgid );
 	fprintf( wfp, "msgid: %ld\n", (long) op->o_msgid );
 	print_suffixes( wfp, be );
 	fprintf( wfp, "dn: %s\n", dn->bv_val );
diff --git a/servers/slapd/back-shell/delete.c b/servers/slapd/back-shell/delete.c
index e675f8062e..6d3b27908d 100644
--- a/servers/slapd/back-shell/delete.c
+++ b/servers/slapd/back-shell/delete.c
@@ -42,7 +42,6 @@ shell_back_delete(
 
 	/* write out the request to the delete process */
 	fprintf( wfp, "DELETE\n" );
-	fprintf( wfp, "opid: %ld/%ld\n", op->o_connid, (long) op->o_msgid );
 	fprintf( wfp, "msgid: %ld\n", (long) op->o_msgid );
 	print_suffixes( wfp, be );
 	fprintf( wfp, "dn: %s\n", dn->bv_val );
diff --git a/servers/slapd/back-shell/modify.c b/servers/slapd/back-shell/modify.c
index 5acddbe994..0b1719eddd 100644
--- a/servers/slapd/back-shell/modify.c
+++ b/servers/slapd/back-shell/modify.c
@@ -45,7 +45,6 @@ shell_back_modify(
 
 	/* write out the request to the modify process */
 	fprintf( wfp, "MODIFY\n" );
-	fprintf( wfp, "opid: %ld/%ld\n", op->o_connid, (long) op->o_msgid );
 	fprintf( wfp, "msgid: %ld\n", (long) op->o_msgid );
 	print_suffixes( wfp, be );
 	fprintf( wfp, "dn: %s\n", dn->bv_val );
diff --git a/servers/slapd/back-shell/modrdn.c b/servers/slapd/back-shell/modrdn.c
index e2c7d64d5f..68bfed9795 100644
--- a/servers/slapd/back-shell/modrdn.c
+++ b/servers/slapd/back-shell/modrdn.c
@@ -60,7 +60,6 @@ shell_back_modrdn(
 
 	/* write out the request to the modrdn process */
 	fprintf( wfp, "MODRDN\n" );
-	fprintf( wfp, "opid: %ld/%ld\n", op->o_connid, (long) op->o_msgid );
 	fprintf( wfp, "msgid: %ld\n", (long) op->o_msgid );
 	print_suffixes( wfp, be );
 	fprintf( wfp, "dn: %s\n", dn->bv_val );
diff --git a/servers/slapd/back-shell/search.c b/servers/slapd/back-shell/search.c
index a1579e20e2..c8c1118172 100644
--- a/servers/slapd/back-shell/search.c
+++ b/servers/slapd/back-shell/search.c
@@ -51,7 +51,6 @@ shell_back_search(
 
 	/* write out the request to the search process */
 	fprintf( wfp, "SEARCH\n" );
-	fprintf( wfp, "opid: %ld/%ld\n", op->o_connid, (long) op->o_msgid );
 	fprintf( wfp, "msgid: %ld\n", (long) op->o_msgid );
 	print_suffixes( wfp, be );
 	fprintf( wfp, "base: %s\n", base->bv_val );
diff --git a/servers/slapd/back-shell/unbind.c b/servers/slapd/back-shell/unbind.c
index a4327f99e2..e54173d7ba 100644
--- a/servers/slapd/back-shell/unbind.c
+++ b/servers/slapd/back-shell/unbind.c
@@ -36,7 +36,6 @@ shell_back_unbind(
 
 	/* write out the request to the unbind process */
 	fprintf( wfp, "UNBIND\n" );
-	fprintf( wfp, "opid: %ld/%ld\n", op->o_connid, (long) op->o_msgid );
 	fprintf( wfp, "msgid: %ld\n", (long) op->o_msgid );
 	print_suffixes( wfp, be );
 	fprintf( wfp, "dn: %s\n", (conn->c_dn.bv_len ? conn->c_dn.bv_val : "") );
diff --git a/servers/slapd/config.c b/servers/slapd/config.c
index 23a4599f06..d89f9cb21b 100644
--- a/servers/slapd/config.c
+++ b/servers/slapd/config.c
@@ -11,6 +11,7 @@
 
 #include <ac/string.h>
 #include <ac/ctype.h>
+#include <ac/signal.h>
 #include <ac/socket.h>
 #include <ac/errno.h>
 
@@ -40,6 +41,7 @@ slap_mask_t		global_disallows = 0;
 slap_mask_t		global_requires = 0;
 slap_ssf_set_t	global_ssf_set;
 char		*replogfile;
+int		global_gentlehup = 0;
 int		global_idletimeout = 0;
 char	*global_host = NULL;
 char	*global_realm = NULL;
@@ -1651,7 +1653,6 @@ read_config( const char *fname )
 				       "%s: line %d: old objectclass format not supported.\n",
 				       fname, lineno, 0 );
 #endif
-
 			}
 
 		/* specify an attribute type */
@@ -2039,6 +2040,22 @@ read_config( const char *fname )
 				}
 			}
 
+#ifdef SIGHUP
+		/* turn on/off gentle SIGHUP handling */
+		} else if ( strcasecmp( cargv[0], "gentlehup" ) == 0 ) {
+			if ( cargc < 2 ) {
+				Debug( LDAP_DEBUG_ANY,
+    "%s: line %d: missing on|off in \"gentlehup <on|off>\" line\n",
+				    fname, lineno, 0 );
+				return( 1 );
+			}
+			if ( strcasecmp( cargv[1], "off" ) == 0 ) {
+				global_gentlehup = 0;
+			} else {
+				global_gentlehup = 1;
+			}
+#endif
+
 		/* set idle timeout value */
 		} else if ( strcasecmp( cargv[0], "idletimeout" ) == 0 ) {
 			int i;
diff --git a/servers/slapd/daemon.c b/servers/slapd/daemon.c
index 2ab0fa769f..3dc4cdbcf1 100644
--- a/servers/slapd/daemon.c
+++ b/servers/slapd/daemon.c
@@ -55,12 +55,12 @@ do { if (w) tcp_write( wake_sds[1], "0", 1 ); } while(0)
 #ifndef HAVE_WINSOCK
 static
 #endif
-volatile sig_atomic_t slapd_shutdown = 0;
+volatile sig_atomic_t slapd_shutdown = 0, slapd_gentle_shutdown = 0;
 
 static struct slap_daemon {
 	ldap_pvt_thread_mutex_t	sd_mutex;
 
-	int sd_nactives;
+	ber_socket_t sd_nactives;
 
 #ifndef HAVE_WINSOCK
 	/* In winsock, accept() returns values higher than dtblsize
@@ -192,6 +192,8 @@ static void slapd_add(ber_socket_t s) {
 	}
 #endif
 
+	slap_daemon.sd_nactives++;
+
 	FD_SET( s, &slap_daemon.sd_actives );
 	FD_SET( s, &slap_daemon.sd_readers );
 
@@ -216,6 +218,8 @@ static void slapd_add(ber_socket_t s) {
 void slapd_remove(ber_socket_t s, int wake) {
 	ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
 
+	slap_daemon.sd_nactives--;
+
 #ifdef NEW_LOGGING
 	LDAP_LOG(( "connection", LDAP_LEVEL_DETAIL1,
 		   "slapd_remove: removing %ld%s%s\n",
@@ -233,7 +237,7 @@ void slapd_remove(ber_socket_t s, int wake) {
 	FD_CLR( s, &slap_daemon.sd_writers );
 
 	ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
-	WAKE_LISTENER(wake);
+	WAKE_LISTENER(wake || slapd_gentle_shutdown < 0);
 }
 
 void slapd_clr_write(ber_socket_t s, int wake) {
@@ -980,6 +984,34 @@ slapd_daemon_destroy(void)
 }
 
 
+static void
+close_listeners(
+	int remove
+)
+{
+	int l;
+
+	for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+		if ( remove )
+			slapd_remove( slap_listeners[l]->sl_sd, 0 );
+		if ( slap_listeners[l]->sl_sd != AC_SOCKET_INVALID ) {
+#ifdef LDAP_PF_LOCAL
+			if ( slap_listeners[l]->sl_sa.sa_addr.sa_family == AF_LOCAL ) {
+				unlink( slap_listeners[l]->sl_sa.sa_un_addr.sun_path );
+			}
+#endif /* LDAP_PF_LOCAL */
+			slapd_close( slap_listeners[l]->sl_sd );
+		}
+		if ( slap_listeners[l]->sl_url )
+			free ( slap_listeners[l]->sl_url );
+		if ( slap_listeners[l]->sl_name )
+			free ( slap_listeners[l]->sl_name );
+		free ( slap_listeners[l] );
+		slap_listeners[l] = NULL;
+	}
+}
+
+
 static void *
 slapd_daemon_task(
 	void *ptr
@@ -1066,6 +1098,26 @@ slapd_daemon_task(
 			}
 		}
 
+#ifdef SIGHUP
+		if( slapd_gentle_shutdown ) {
+			ber_socket_t active;
+
+			if( slapd_gentle_shutdown > 0 ) {
+				Debug( LDAP_DEBUG_ANY, "slapd gentle shutdown\n", 0, 0, 0 );
+				close_listeners( 1 );
+				slapd_gentle_shutdown = -1;
+			}
+
+			ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
+			active = slap_daemon.sd_nactives;
+			ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
+			if( active == 0 ) {
+				slapd_shutdown = -1;
+				break;
+			}
+		}
+#endif
+
 		FD_ZERO( &writefds );
 		FD_ZERO( &readfds );
 
@@ -1716,21 +1768,8 @@ slapd_daemon_task(
 #endif
 	}
 
-	for ( l = 0; slap_listeners[l] != NULL; l++ ) {
-		if ( slap_listeners[l]->sl_sd != AC_SOCKET_INVALID ) {
-#ifdef LDAP_PF_LOCAL
-			if ( slap_listeners[l]->sl_sa.sa_addr.sa_family == AF_LOCAL ) {
-				unlink( slap_listeners[l]->sl_sa.sa_un_addr.sun_path );
-			}
-#endif /* LDAP_PF_LOCAL */
-			slapd_close( slap_listeners[l]->sl_sd );
-		}
-		if ( slap_listeners[l]->sl_url )
-			free ( slap_listeners[l]->sl_url );
-		if ( slap_listeners[l]->sl_name )
-			free ( slap_listeners[l]->sl_name );
-		free ( slap_listeners[l] );
-	}
+	if( slapd_gentle_shutdown >= 0 )
+		close_listeners ( 0 );
 	free ( slap_listeners );
 	slap_listeners = NULL;
 
@@ -1862,6 +1901,11 @@ slap_sig_shutdown( int sig )
 		  0, 0, 0);
 #endif
 	else
+#endif
+#ifdef SIGHUP
+	if (sig == SIGHUP && global_gentlehup && slapd_gentle_shutdown == 0)
+		slapd_gentle_shutdown = 1;
+	else
 #endif
 	slapd_shutdown = 1;
 
diff --git a/servers/slapd/matchedValues.c b/servers/slapd/matchedValues.c
index ac953f111e..56bd29667b 100644
--- a/servers/slapd/matchedValues.c
+++ b/servers/slapd/matchedValues.c
@@ -23,7 +23,7 @@ static int test_mra_vrFilter(
 	Backend 	*be,
 	Connection 	*conn,
 	Operation	*op,
-	Entry		*e,
+	Attribute	*a,
 	MatchingRuleAssertion *mra,
 	char 		***e_flags
 );
@@ -33,7 +33,7 @@ test_substrings_vrFilter(
 	Backend		*be,
 	Connection	*conn,
 	Operation	*op,
-	Entry		*e,
+	Attribute	*a,
 	ValuesReturnFilter *f,
 	char		***e_flags
 );
@@ -43,7 +43,7 @@ test_presence_vrFilter(
 	Backend		*be,
 	Connection	*conn,
 	Operation	*op,
-	Entry		*e,
+	Attribute	*a,
 	AttributeDescription *desc,
 	char 		***e_flags
 );
@@ -53,7 +53,7 @@ test_ava_vrFilter(
 	Backend		*be,
 	Connection	*conn,
 	Operation	*op,
-	Entry		*e,
+	Attribute	*a,
 	AttributeAssertion *ava,
 	int		type,
 	char 		***e_flags
@@ -65,7 +65,7 @@ filter_matched_values(
 	Backend		*be,
 	Connection	*conn,
 	Operation	*op,
-	Entry		*e,
+	Attribute	*a,
 	char		***e_flags
 )
 {
@@ -108,7 +108,7 @@ filter_matched_values(
 #else
 			Debug( LDAP_DEBUG_FILTER, "	EQUALITY\n", 0, 0, 0 );
 #endif
-			rc = test_ava_vrFilter( be, conn, op, e, f->f_ava,
+			rc = test_ava_vrFilter( be, conn, op, a, f->f_ava,
 				LDAP_FILTER_EQUALITY, e_flags );
 			if( rc == -1 ) {
 				return rc;
@@ -123,7 +123,7 @@ filter_matched_values(
 			Debug( LDAP_DEBUG_FILTER, "	SUBSTRINGS\n", 0, 0, 0 );
 #endif
 
-			rc = test_substrings_vrFilter( be, conn, op, e,
+			rc = test_substrings_vrFilter( be, conn, op, a,
 				f, e_flags );
 			if( rc == -1 ) {
 				return rc;
@@ -137,7 +137,7 @@ filter_matched_values(
 #else
 			Debug( LDAP_DEBUG_FILTER, "	PRESENT\n", 0, 0, 0 );
 #endif
-			rc = test_presence_vrFilter( be, conn, op, e,
+			rc = test_presence_vrFilter( be, conn, op, a,
 				f->f_desc, e_flags );
 			if( rc == -1 ) {
 				return rc;
@@ -145,7 +145,7 @@ filter_matched_values(
 			break;
 
 		case LDAP_FILTER_GE:
-			rc = test_ava_vrFilter( be, conn, op, e, f->f_ava,
+			rc = test_ava_vrFilter( be, conn, op, a, f->f_ava,
 				LDAP_FILTER_GE, e_flags );
 			if( rc == -1 ) {
 				return rc;
@@ -153,7 +153,7 @@ filter_matched_values(
 			break;
 
 		case LDAP_FILTER_LE:
-			rc = test_ava_vrFilter( be, conn, op, e, f->f_ava,
+			rc = test_ava_vrFilter( be, conn, op, a, f->f_ava,
 				LDAP_FILTER_LE, e_flags );
 			if( rc == -1 ) {
 				return rc;
@@ -167,7 +167,7 @@ filter_matched_values(
 #else
 			Debug( LDAP_DEBUG_FILTER, "	EXT\n", 0, 0, 0 );
 #endif
-			rc = test_mra_vrFilter( be, conn, op, e,
+			rc = test_mra_vrFilter( be, conn, op, a,
 				f->f_mra, e_flags );
 			if( rc == -1 ) {
 				return rc;
@@ -198,25 +198,18 @@ filter_matched_values(
 
 static int
 test_ava_vrFilter(
-		Backend		*be,
+	Backend		*be,
 	Connection	*conn,
-		Operation	*op,
-		Entry		*e,
+	Operation	*op,
+	Attribute	*a,
 	AttributeAssertion *ava,
-		int		type,
-		char 		***e_flags
+	int		type,
+	char 		***e_flags
 )
 {
 	int 		i, j;
-	Attribute	*a;
-
-	if ( !access_allowed( be, conn, op, e,
-		ava->aa_desc, &ava->aa_value, ACL_SEARCH, NULL ) )
-	{
-		return LDAP_INSUFFICIENT_ACCESS;
-	}
 
-	for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+	for ( i=0; a != NULL; a = a->a_next, i++ ) {
 
 		MatchingRule *mr;
 		struct berval *bv;
@@ -287,22 +280,17 @@ test_ava_vrFilter(
 
 static int
 test_presence_vrFilter(
-		Backend		*be,
-		Connection	*conn,
-		Operation	*op,
-		Entry		*e,
-		AttributeDescription *desc,
-		char 		***e_flags
+	Backend		*be,
+	Connection	*conn,
+	Operation	*op,
+	Attribute	*a,
+	AttributeDescription *desc,
+	char 		***e_flags
 )
 {
 	int i, j;
-	Attribute	*a;
 
-	if ( !access_allowed( be, conn, op, e, desc, NULL, ACL_SEARCH, NULL ) ) {
-		return LDAP_INSUFFICIENT_ACCESS;
-	}
-
-	for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+	for ( i=0; a != NULL; a = a->a_next, i++ ) {
 		struct berval *bv;
 
 		if ( !is_ad_subtype( a->a_desc, desc ) ) {
@@ -318,24 +306,17 @@ test_presence_vrFilter(
 
 static int
 test_substrings_vrFilter(
-		Backend		*be,
-		Connection	*conn,
-		Operation	*op,
-		Entry		*e,
-		ValuesReturnFilter *f,
-		char		***e_flags
+	Backend		*be,
+	Connection	*conn,
+	Operation	*op,
+	Attribute	*a,
+	ValuesReturnFilter *f,
+	char		***e_flags
 )
 {
 	int i, j;
-	Attribute	*a;
-
-	if ( !access_allowed( be, conn, op, e,
-		f->f_sub_desc, NULL, ACL_SEARCH, NULL ) )
-	{
-		return LDAP_INSUFFICIENT_ACCESS;
-	}
 
-	for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+	for ( i=0; a != NULL; a = a->a_next, i++ ) {
 		MatchingRule *mr = a->a_desc->ad_type->sat_substr;
 		struct berval *bv;
 
@@ -373,21 +354,14 @@ static int test_mra_vrFilter(
 	Backend 	*be,
 	Connection 	*conn,
 	Operation	*op,
-	Entry		*e,
+	Attribute	*a,
 	MatchingRuleAssertion *mra,
 	char 		***e_flags
 )
 {
 	int i, j;
-	Attribute	*a;
-
-	if( !access_allowed( be, conn, op, e,
-		mra->ma_desc, &mra->ma_value, ACL_SEARCH, NULL ) )
-	{
-		return LDAP_INSUFFICIENT_ACCESS;
-	}
 
-	for (a = e->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+	for ( i=0; a != NULL; a = a->a_next, i++ ) {
 		struct berval *bv;
 	
 		if ( !is_ad_subtype( a->a_desc, mra->ma_desc ) ) {
diff --git a/servers/slapd/mr.c b/servers/slapd/mr.c
index e1af29d0c8..f8e788b6e0 100644
--- a/servers/slapd/mr.c
+++ b/servers/slapd/mr.c
@@ -108,7 +108,7 @@ mr_insert(
 				 (AVL_DUP) avl_dup_error ) ) {
 			*err = smr->smr_oid;
 			ldap_memfree(mir);
-			return SLAP_SCHERR_DUP_RULE;
+			return SLAP_SCHERR_MR_DUP;
 		}
 		/* FIX: temporal consistency check */
 		mr_bvfind(&mir->mir_name);
@@ -125,7 +125,7 @@ mr_insert(
 					 (AVL_DUP) avl_dup_error ) ) {
 				*err = *names;
 				ldap_memfree(mir);
-				return SLAP_SCHERR_DUP_RULE;
+				return SLAP_SCHERR_MR_DUP;
 			}
 			/* FIX: temporal consistency check */
 			mr_bvfind(&mir->mir_name);
diff --git a/servers/slapd/oc.c b/servers/slapd/oc.c
new file mode 100644
index 0000000000..02f6ea4022
--- /dev/null
+++ b/servers/slapd/oc.c
@@ -0,0 +1,512 @@
+/* oc.c - object class routines */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "ldap_pvt.h"
+
+int is_object_subclass(
+	ObjectClass *sup,
+	ObjectClass *sub )
+{
+	int i;
+
+	if( sub == NULL || sup == NULL ) return 0;
+
+#if 1
+	Debug( LDAP_DEBUG_TRACE, "is_object_subclass(%s,%s) %d\n",
+		sup->soc_oid, sub->soc_oid, sup == sub );
+#endif
+
+	if( sup == sub ) {
+		return 1;
+	}
+
+	if( sub->soc_sups == NULL ) {
+		return 0;
+	}
+
+	for( i=0; sub->soc_sups[i] != NULL; i++ ) {
+		if( is_object_subclass( sup, sub->soc_sups[i] ) ) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+int is_entry_objectclass(
+	Entry*	e,
+	ObjectClass *oc,
+	int set_flags )
+{
+	Attribute *attr;
+	struct berval *bv;
+	AttributeDescription *objectClass = slap_schema.si_ad_objectClass;
+	assert(!( e == NULL || oc == NULL ));
+
+	if( e == NULL || oc == NULL ) {
+		return 0;
+	}
+
+	if( set_flags && ( e->e_ocflags & SLAP_OC__END )) {
+		return (e->e_ocflags & oc->soc_flags) ? 1 : 0;
+	}
+
+	/*
+	 * find objectClass attribute
+	 */
+	attr = attr_find(e->e_attrs, objectClass);
+
+	if( attr == NULL ) {
+		/* no objectClass attribute */
+#ifdef NEW_LOGGING
+		LDAP_LOG(( "operation", LDAP_LEVEL_ERR, "is_entry_objectclass: "
+			"dn(%s), oid (%s), no objectClass attribute.\n",
+			e->e_dn == NULL ? "" : e->e_dn,
+			oc->soc_oclass.oc_oid ));
+#else
+		Debug( LDAP_DEBUG_ANY, "is_entry_objectclass(\"%s\", \"%s\") "
+			"no objectClass attribute\n",
+			e->e_dn == NULL ? "" : e->e_dn,
+			oc->soc_oclass.oc_oid, 0 );
+#endif
+
+		return 0;
+	}
+
+	for( bv=attr->a_vals; bv->bv_val; bv++ ) {
+		ObjectClass *objectClass = oc_bvfind( bv );
+
+		if ( objectClass == oc && !set_flags ) {
+			return 1;
+		}
+		
+		if ( objectClass != NULL ) {
+			e->e_ocflags |= objectClass->soc_flags;
+		}
+	}
+	e->e_ocflags |= SLAP_OC__END;	/* We've finished this */
+
+	return (e->e_ocflags & oc->soc_flags);
+}
+
+
+struct oindexrec {
+	struct berval	oir_name;
+	ObjectClass	*oir_oc;
+};
+
+static Avlnode	*oc_index = NULL;
+static ObjectClass *oc_list = NULL;
+
+static int
+oc_index_cmp(
+    struct oindexrec	*oir1,
+    struct oindexrec	*oir2 )
+{
+	int i = oir1->oir_name.bv_len - oir2->oir_name.bv_len;
+	if (i)
+		return i;
+	return strcasecmp( oir1->oir_name.bv_val, oir2->oir_name.bv_val );
+}
+
+static int
+oc_index_name_cmp(
+    struct berval	*name,
+    struct oindexrec	*oir )
+{
+	int i = name->bv_len - oir->oir_name.bv_len;
+	if (i)
+		return i;
+	return strncasecmp( name->bv_val, oir->oir_name.bv_val, name->bv_len );
+}
+
+ObjectClass *
+oc_find( const char *ocname )
+{
+	struct berval bv;
+
+	bv.bv_val = (char *)ocname;
+	bv.bv_len = strlen( ocname );
+
+	return( oc_bvfind( &bv ) );
+}
+
+ObjectClass *
+oc_bvfind( struct berval *ocname )
+{
+	struct oindexrec	*oir;
+
+	oir = (struct oindexrec *) avl_find( oc_index, ocname,
+            (AVL_CMP) oc_index_name_cmp );
+
+	if ( oir != NULL ) {
+		return( oir->oir_oc );
+	}
+
+	return( NULL );
+}
+
+static int
+oc_create_required(
+    ObjectClass		*soc,
+    char		**attrs,
+	int			*op,
+    const char		**err )
+{
+	char		**attrs1;
+	AttributeType	*sat;
+	AttributeType	**satp;
+	int		i;
+
+	if ( attrs ) {
+		attrs1 = attrs;
+		while ( *attrs1 ) {
+			sat = at_find(*attrs1);
+			if ( !sat ) {
+				*err = *attrs1;
+				return SLAP_SCHERR_ATTR_NOT_FOUND;
+			}
+
+			if( is_at_operational( sat )) (*op)++;
+
+			if ( at_find_in_list(sat, soc->soc_required) < 0) {
+				if ( at_append_to_list(sat, &soc->soc_required) ) {
+					*err = *attrs1;
+					return SLAP_SCHERR_OUTOFMEM;
+				}
+			}
+			attrs1++;
+		}
+		/* Now delete duplicates from the allowed list */
+		for ( satp = soc->soc_required; *satp; satp++ ) {
+			i = at_find_in_list(*satp,soc->soc_allowed);
+			if ( i >= 0 ) {
+				at_delete_from_list(i, &soc->soc_allowed);
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+oc_create_allowed(
+    ObjectClass		*soc,
+    char		**attrs,
+	int			*op,
+    const char		**err )
+{
+	char		**attrs1;
+	AttributeType	*sat;
+
+	if ( attrs ) {
+		attrs1 = attrs;
+		while ( *attrs1 ) {
+			sat = at_find(*attrs1);
+			if ( !sat ) {
+				*err = *attrs1;
+				return SLAP_SCHERR_ATTR_NOT_FOUND;
+			}
+
+			if( is_at_operational( sat )) (*op)++;
+
+			if ( at_find_in_list(sat, soc->soc_required) < 0 &&
+			     at_find_in_list(sat, soc->soc_allowed) < 0 ) {
+				if ( at_append_to_list(sat, &soc->soc_allowed) ) {
+					*err = *attrs1;
+					return SLAP_SCHERR_OUTOFMEM;
+				}
+			}
+			attrs1++;
+		}
+	}
+	return 0;
+}
+
+static int
+oc_add_sups(
+    ObjectClass		*soc,
+    char			**sups,
+	int			*op,
+    const char		**err )
+{
+	int		code;
+	ObjectClass	*soc1;
+	int		nsups;
+	char	**sups1;
+	int		add_sups = 0;
+
+	if ( sups ) {
+		if ( !soc->soc_sups ) {
+			/* We are at the first recursive level */
+			add_sups = 1;
+			nsups = 1;
+			sups1 = sups;
+			while ( *sups1 ) {
+				nsups++;
+				sups1++;
+			}
+			soc->soc_sups = (ObjectClass **)ch_calloc(nsups,
+					  sizeof(ObjectClass *));
+		}
+
+		nsups = 0;
+		sups1 = sups;
+		while ( *sups1 ) {
+			soc1 = oc_find(*sups1);
+			if ( !soc1 ) {
+				*err = *sups1;
+				return SLAP_SCHERR_CLASS_NOT_FOUND;
+			}
+
+			/* check object class usage
+			 * abstract classes can only sup abstract classes 
+			 * structural classes can not sup auxiliary classes
+			 * auxiliary classes can not sup structural classes
+			 */
+			if( soc->soc_kind != soc1->soc_kind
+				&& soc1->soc_kind != LDAP_SCHEMA_ABSTRACT )
+			{
+				*err = *sups1;
+				return SLAP_SCHERR_CLASS_BAD_USAGE;
+			}
+
+			if( soc->soc_flags & SLAP_OC_OPERATIONAL ) (*op)++;
+
+			if ( add_sups ) {
+				soc->soc_sups[nsups] = soc1;
+			}
+
+			code = oc_add_sups( soc, soc1->soc_sup_oids, op, err );
+			if ( code ) return code;
+
+			code = oc_create_required( soc, soc1->soc_at_oids_must, op, err );
+			if ( code ) return code;
+
+			code = oc_create_allowed( soc, soc1->soc_at_oids_may, op, err );
+			if ( code ) return code;
+
+			nsups++;
+			sups1++;
+		}
+	}
+
+	return 0;
+}
+
+void
+oc_destroy( void )
+{
+	ObjectClass *o, *n;
+
+	avl_free(oc_index, ldap_memfree);
+	for (o=oc_list; o; o=n)
+	{
+		n = o->soc_next;
+		if (o->soc_sups) ldap_memfree(o->soc_sups);
+		if (o->soc_required) ldap_memfree(o->soc_required);
+		if (o->soc_allowed) ldap_memfree(o->soc_allowed);
+		ldap_objectclass_free((LDAPObjectClass *)o);
+	}
+}
+
+static int
+oc_insert(
+    ObjectClass		*soc,
+    const char		**err
+)
+{
+	ObjectClass	**ocp;
+	struct oindexrec	*oir;
+	char			**names;
+
+	ocp = &oc_list;
+	while ( *ocp != NULL ) {
+		ocp = &(*ocp)->soc_next;
+	}
+	*ocp = soc;
+
+	if ( soc->soc_oid ) {
+		oir = (struct oindexrec *)
+			ch_calloc( 1, sizeof(struct oindexrec) );
+		oir->oir_name.bv_val = soc->soc_oid;
+		oir->oir_name.bv_len = strlen( soc->soc_oid );
+		oir->oir_oc = soc;
+
+		assert( oir->oir_name.bv_val );
+		assert( oir->oir_oc );
+
+		if ( avl_insert( &oc_index, (caddr_t) oir,
+				 (AVL_CMP) oc_index_cmp,
+				 (AVL_DUP) avl_dup_error ) )
+		{
+			*err = soc->soc_oid;
+			ldap_memfree(oir);
+			return SLAP_SCHERR_CLASS_DUP;
+		}
+
+		/* FIX: temporal consistency check */
+		assert( oc_bvfind(&oir->oir_name) != NULL );
+	}
+
+	if ( (names = soc->soc_names) ) {
+		while ( *names ) {
+			oir = (struct oindexrec *)
+				ch_calloc( 1, sizeof(struct oindexrec) );
+			oir->oir_name.bv_val = *names;
+			oir->oir_name.bv_len = strlen( *names );
+			oir->oir_oc = soc;
+
+			assert( oir->oir_name.bv_val );
+			assert( oir->oir_oc );
+
+			if ( avl_insert( &oc_index, (caddr_t) oir,
+					 (AVL_CMP) oc_index_cmp,
+					 (AVL_DUP) avl_dup_error ) )
+			{
+				*err = *names;
+				ldap_memfree(oir);
+				return SLAP_SCHERR_CLASS_DUP;
+			}
+
+			/* FIX: temporal consistency check */
+			assert( oc_bvfind(&oir->oir_name) != NULL );
+
+			names++;
+		}
+	}
+
+	return 0;
+}
+
+int
+oc_add(
+    LDAPObjectClass	*oc,
+	int user,
+    const char		**err
+)
+{
+	ObjectClass	*soc;
+	int		code;
+	int		op = 0;
+
+	if ( oc->oc_names != NULL ) {
+		int i;
+
+		for( i=0; oc->oc_names[i]; i++ ) {
+			if( !slap_valid_descr( oc->oc_names[i] ) ) {
+				return SLAP_SCHERR_BAD_DESCR;
+			}
+		}
+	}
+
+	if ( !OID_LEADCHAR( oc->oc_oid[0] )) {
+		/* Expand OID macros */
+		char *oid = oidm_find( oc->oc_oid );
+		if ( !oid ) {
+			*err = oc->oc_oid;
+			return SLAP_SCHERR_OIDM;
+		}
+		if ( oid != oc->oc_oid ) {
+			ldap_memfree( oc->oc_oid );
+			oc->oc_oid = oid;
+		}
+	}
+
+	soc = (ObjectClass *) ch_calloc( 1, sizeof(ObjectClass) );
+	AC_MEMCPY( &soc->soc_oclass, oc, sizeof(LDAPObjectClass) );
+
+	if( soc->soc_sup_oids == NULL &&
+		soc->soc_kind == LDAP_SCHEMA_STRUCTURAL )
+	{
+		/* structural object classes implicitly inherit from 'top' */
+		static char *top_oids[] = { SLAPD_TOP_OID, NULL };
+		code = oc_add_sups( soc, top_oids, &op, err );
+	} else {
+		code = oc_add_sups( soc, soc->soc_sup_oids, &op, err );
+	}
+
+	if ( code != 0 ) return code;
+
+	code = oc_create_required( soc, soc->soc_at_oids_must, &op, err );
+	if ( code != 0 ) return code;
+
+	code = oc_create_allowed( soc, soc->soc_at_oids_may, &op, err );
+	if ( code != 0 ) return code;
+
+	if( user && op ) return SLAP_SCHERR_CLASS_BAD_SUP;
+
+	code = oc_insert(soc,err);
+	return code;
+}
+
+#ifdef LDAP_DEBUG
+
+static void
+oc_print( ObjectClass *oc )
+{
+	int	i;
+	const char *mid;
+
+	printf( "objectclass %s\n", ldap_objectclass2name( &oc->soc_oclass ) );
+	if ( oc->soc_required != NULL ) {
+		mid = "\trequires ";
+		for ( i = 0; oc->soc_required[i] != NULL; i++, mid = "," )
+			printf( "%s%s", mid,
+			        ldap_attributetype2name( &oc->soc_required[i]->sat_atype ) );
+		printf( "\n" );
+	}
+	if ( oc->soc_allowed != NULL ) {
+		mid = "\tallows ";
+		for ( i = 0; oc->soc_allowed[i] != NULL; i++, mid = "," )
+			printf( "%s%s", mid,
+			        ldap_attributetype2name( &oc->soc_allowed[i]->sat_atype ) );
+		printf( "\n" );
+	}
+}
+
+#endif
+
+
+#if defined( SLAPD_SCHEMA_DN )
+
+int
+oc_schema_info( Entry *e )
+{
+	struct berval	vals[2];
+	ObjectClass	*oc;
+
+	AttributeDescription *ad_objectClasses = slap_schema.si_ad_objectClasses;
+
+	vals[1].bv_val = NULL;
+
+	for ( oc = oc_list; oc; oc = oc->soc_next ) {
+		if ( ldap_objectclass2bv( &oc->soc_oclass, vals ) == NULL ) {
+			return -1;
+		}
+
+		if( oc->soc_flags & SLAP_OC_HIDE ) continue;
+
+#if 0
+		Debug( LDAP_DEBUG_TRACE, "Merging oc [%ld] %s\n",
+	       (long) vals[0].bv_len, vals[0].bv_val, 0 );
+#endif
+		attr_merge( e, ad_objectClasses, vals );
+		ldap_memfree( vals[0].bv_val );
+	}
+	return 0;
+}
+
+#endif
diff --git a/servers/slapd/passwd.c b/servers/slapd/passwd.c
index 3a8ddd9c1d..94cbeac0cf 100644
--- a/servers/slapd/passwd.c
+++ b/servers/slapd/passwd.c
@@ -321,13 +321,17 @@ slap_passwd_hash(
 #endif
 
 	tmp = lutil_passwd_hash( cred , hash );
-	assert( tmp != NULL );
 	
 #if defined( SLAPD_CRYPT ) || defined( SLAPD_SPASSWD )
 	ldap_pvt_thread_mutex_unlock( &passwd_mutex );
 #endif
+
+	if( tmp == NULL ) {
+		new->bv_len = 0;
+		new->bv_val = NULL;
+	}
+
 	*new = *tmp;
 	free( tmp );
-
 	return;
 }
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index 7b2a65a980..f80e53969a 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -526,7 +526,7 @@ LDAP_SLAPD_F (int) filter_matched_values(
 	Backend		*be,
 	Connection	*conn,
 	Operation	*op,
-	Entry		*e,
+	Attribute	*a,
 	char		***e_flags );
 
 /*
@@ -626,6 +626,7 @@ LDAP_SLAPD_F (void) mra_free LDAP_P((
 /* oc.c */
 LDAP_SLAPD_F (int) oc_add LDAP_P((
 	LDAPObjectClass *oc,
+	int user,
 	const char **err));
 LDAP_SLAPD_F (void) oc_destroy LDAP_P(( void ));
 
@@ -1016,6 +1017,7 @@ LDAP_SLAPD_V (const char) 	Versionstr[];
 LDAP_SLAPD_V (struct slap_limits_set)		deflimit;
 
 LDAP_SLAPD_V (slap_access_t)	global_default_access;
+LDAP_SLAPD_V (int)		global_gentlehup;
 LDAP_SLAPD_V (int)		global_idletimeout;
 LDAP_SLAPD_V (int)		global_schemacheck;
 LDAP_SLAPD_V (char *)	global_host;
diff --git a/servers/slapd/result.c b/servers/slapd/result.c
index 83906f6418..140e4a6d6c 100644
--- a/servers/slapd/result.c
+++ b/servers/slapd/result.c
@@ -744,9 +744,9 @@ send_search_entry(
 		e_flags[i] = a_flags; 
 	}
 
-	if ( op->vrFilter != NULL ){
+	if ( op->vrFilter != NULL ){ 
 
-		rc = filter_matched_values(be, conn, op, e, &e_flags) ; 
+		rc = filter_matched_values(be, conn, op, e->e_attrs, &e_flags) ; 
 	    
 		if ( rc == -1 ) {
 #ifdef NEW_LOGGING
@@ -917,8 +917,51 @@ send_search_entry(
 	/* eventually will loop through generated operational attributes */
 	/* only have subschemaSubentry implemented */
 	aa = backend_operational( be, conn, op, e, attrs, opattrs );
+
+	for ( a = aa, i=0; a != NULL; a = a->a_next ) i++;
+	e_flags = ch_malloc ( i * sizeof(a_flags) );
 	
-	for (a = aa ; a != NULL; a = a->a_next ) {
+	for ( a = aa, i=0; a != NULL; a = a->a_next, i++ ) {
+		for ( j = 0; a->a_vals[j].bv_val != NULL; j++ );
+
+		a_flags = ch_calloc ( j, sizeof(char) );
+		/* If no ValuesReturnFilter control return everything */
+		if ( op->vrFilter == NULL ){
+		    memset(a_flags, 1, j);
+		}
+		e_flags[i] = a_flags; 
+	}
+
+	if ( op->vrFilter != NULL ){ 
+
+		rc = filter_matched_values(be, conn, op, aa, &e_flags) ; 
+	    
+		if ( rc == -1 ) {
+#ifdef NEW_LOGGING
+			LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+				"send_search_entry: conn %lu "
+				"matched values filtering failed\n",
+				conn ? conn->c_connid : 0 ));
+#else
+	    	Debug( LDAP_DEBUG_ANY,
+				"matched values filtering failed\n", 0, 0, 0 );
+#endif
+			ber_free( ber, 1 );
+
+			/* free e_flags */
+			for ( a = aa, i=0; a != NULL; a = a->a_next, i++ ) {
+				free( e_flags[i] );
+			}
+			free( e_flags );
+
+			send_ldap_result( conn, op, LDAP_OTHER,
+				NULL, "matched values filtering error", 
+				NULL, NULL );
+			goto error_return;
+		}
+	}
+
+	for (a = aa, j=0; a != NULL; a = a->a_next, j++ ) {
 		AttributeDescription *desc = a->a_desc;
 
 		if ( attrs == NULL ) {
@@ -974,6 +1017,12 @@ send_search_entry(
 			ber_free_buf( ber );
 			send_ldap_result( conn, op, LDAP_OTHER,
 			    NULL, "encoding description error", NULL, NULL );
+			/* free e_flags */
+			for ( a = aa, i=0; a != NULL; a = a->a_next, i++ ) {
+				free( e_flags[i] );
+			}
+			free( e_flags );
+
 			attrs_free( aa );
 			goto error_return;
 		}
@@ -1000,6 +1049,10 @@ send_search_entry(
 					continue;
 				}
 
+				if ( e_flags[j][i] == 0 ){
+					continue;
+				}
+
 				if (( rc = ber_printf( ber, "O", &a->a_vals[i] )) == -1 ) {
 #ifdef NEW_LOGGING
 					LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
@@ -1016,6 +1069,12 @@ send_search_entry(
 					send_ldap_result( conn, op, LDAP_OTHER,
 						NULL, "encoding values error", 
 						NULL, NULL );
+					/* free e_flags */
+					for ( a = aa, i=0; a != NULL; a = a->a_next, i++ ) {
+						free( e_flags[i] );
+					}
+					free( e_flags );
+
 					attrs_free( aa );
 					goto error_return;
 				}
@@ -1035,13 +1094,24 @@ send_search_entry(
 			ber_free_buf( ber );
 			send_ldap_result( conn, op, LDAP_OTHER,
 			    NULL, "encode end error", NULL, NULL );
+			/* free e_flags */
+			for ( a = aa, i=0; a != NULL; a = a->a_next, i++ ) {
+				free( e_flags[i] );
+			}
+			free( e_flags );
+
 			attrs_free( aa );
 			goto error_return;
 		}
 	}
 
-	attrs_free( aa );
+	/* free e_flags */
+	for ( a = aa, i=0; a != NULL; a = a->a_next, i++ ) {
+		free( e_flags[i] );
+	}
+	free( e_flags );
 
+	attrs_free( aa );
 	rc = ber_printf( ber, /*{{{*/ "}N}N}" );
 
 #ifdef LDAP_CONNECTIONLESS
diff --git a/servers/slapd/schema_prep.c b/servers/slapd/schema_prep.c
index 33d11278f7..184aad8d15 100644
--- a/servers/slapd/schema_prep.c
+++ b/servers/slapd/schema_prep.c
@@ -162,28 +162,29 @@ static struct slap_schema_oc_map {
 			"NAME 'extensibleObject' "
 			"DESC 'RFC2252: extensible object' "
 			"SUP top AUXILIARY )",
-		0, 0, offsetof(struct slap_internal_schema, si_oc_extensibleObject) },
+		0, SLAP_OC_OPERATIONAL,
+		offsetof(struct slap_internal_schema, si_oc_extensibleObject) },
 	{ "alias", "( 2.5.6.1 NAME 'alias' "
 			"DESC 'RFC2256: an alias' "
 			"SUP top STRUCTURAL "
 			"MUST aliasedObjectName )",
-		aliasObjectClass, SLAP_OC_ALIAS,
+		aliasObjectClass, SLAP_OC_ALIAS|SLAP_OC_OPERATIONAL,
 		offsetof(struct slap_internal_schema, si_oc_alias) },
 	{ "referral", "( 2.16.840.1.113730.3.2.6 NAME 'referral' "
 			"DESC 'namedref: named subordinate referral' "
 			"SUP top STRUCTURAL MUST ref )",
-		referralObjectClass, SLAP_OC_REFERRAL,
+		referralObjectClass, SLAP_OC_REFERRAL|SLAP_OC_OPERATIONAL,
 		offsetof(struct slap_internal_schema, si_oc_referral) },
 	{ "LDAProotDSE", "( 1.3.6.1.4.1.4203.1.4.1 "
 			"NAME ( 'OpenLDAProotDSE' 'LDAProotDSE' ) "
 			"DESC 'OpenLDAP Root DSE object' "
 			"SUP top STRUCTURAL MAY cn )",
-		rootDseObjectClass, 0,
+		rootDseObjectClass, SLAP_OC_OPERATIONAL,
 		offsetof(struct slap_internal_schema, si_oc_rootdse) },
 	{ "subentry", "( 2.5.20.0 NAME 'subentry' "
 			"SUP top STRUCTURAL "
 			"MUST ( cn $ subtreeSpecification ) )",
-		subentryObjectClass, SLAP_OC_SUBENTRY,
+		subentryObjectClass, SLAP_OC_SUBENTRY|SLAP_OC_OPERATIONAL,
 		offsetof(struct slap_internal_schema, si_oc_subentry) },
 	{ "subschema", "( 2.5.20.1 NAME 'subschema' "
 		"DESC 'RFC2252: controlling subschema (sub)entry' "
@@ -191,13 +192,14 @@ static struct slap_schema_oc_map {
 		"MAY ( dITStructureRules $ nameForms $ ditContentRules $ "
 			"objectClasses $ attributeTypes $ matchingRules $ "
 			"matchingRuleUse ) )",
-		subentryObjectClass, 0,
+		subentryObjectClass, SLAP_OC_OPERATIONAL,
 		offsetof(struct slap_internal_schema, si_oc_subschema) },
 	{ "monitor", "( 1.3.6.1.4.1.4203.666.3.2 NAME 'monitor' "
 		"DESC 'OpenLDAP system monitoring' "
 		"STRUCTURAL "
 		"MUST cn )",
-		0, 0, offsetof(struct slap_internal_schema, si_oc_monitor) },
+		0, SLAP_OC_OPERATIONAL,
+		offsetof(struct slap_internal_schema, si_oc_monitor) },
 	{ NULL, NULL, NULL, 0, 0 }
 };
 
@@ -222,7 +224,7 @@ static struct slap_schema_ad_map {
 			"DESC 'RFC2256: object classes of the entity' "
 			"EQUALITY objectIdentifierMatch "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
-		NULL, 0, objectClassMatch, NULL, NULL,
+		NULL, SLAP_AT_FINAL, objectClassMatch, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_objectClass) },
 
 	/* user entry operational attributes */
@@ -278,6 +280,21 @@ static struct slap_schema_ad_map {
 		NULL, 0, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_subschemaSubentry) },
 
+	{ "entryUUID", "( 1.3.6.1.4.1.4203.666.1.6 NAME 'entryUUID' "   
+			"DESC 'LCUP/LDUP: universally unique identifier' "
+			"EQUALITY octetStringMatch "
+			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} "
+			"SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+		NULL, SLAP_AT_HIDE, NULL, NULL, NULL,
+		offsetof(struct slap_internal_schema, si_ad_entryUUID) },
+	{ "entryCSN", "( 1.3.6.1.4.1.4203.666.1.7 NAME 'entryCSN' "
+			"DESC 'LCUP/LDUP: change sequence number' "
+			"EQUALITY octetStringMatch "
+			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} "
+			"SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+		NULL, SLAP_AT_HIDE, NULL, NULL, NULL,
+		offsetof(struct slap_internal_schema, si_ad_entryCSN) },
+
 	/* root DSE attributes */
 	{ "altServer", "( 1.3.6.1.4.1.1466.101.120.6 NAME 'altServer' "
 			"DESC 'RFC2252: alternative servers' "
@@ -344,13 +361,13 @@ static struct slap_schema_ad_map {
 			"EQUALITY objectIdentifierMatch "
 			"USAGE directoryOperation "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
-		administrativeRoleAttribute, 0, NULL, NULL, NULL,
+		administrativeRoleAttribute, SLAP_AT_HIDE, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_administrativeRole) },
 	{ "subtreeSpecification", "( 2.5.18.6 NAME 'subtreeSpecification' "
 			"SINGLE-VALUE "
 			"USAGE directoryOperation "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.45 )",
-		subentryAttribute, 0, NULL, NULL, NULL,
+		subentryAttribute, SLAP_AT_HIDE, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_subtreeSpecification) },
 
 	/* subschema subentry attributes */
@@ -359,13 +376,13 @@ static struct slap_schema_ad_map {
 			"EQUALITY integerFirstComponentMatch "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.17 "
 			"USAGE directoryOperation ) ",
-		subentryAttribute, 0, NULL, NULL, NULL,
+		subentryAttribute, SLAP_AT_HIDE, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_ditStructureRules) },
 	{ "ditContentRules", "( 2.5.21.2 NAME 'dITContentRules' "
 			"DESC 'RFC2252: DIT content rules' "
 			"EQUALITY objectIdentifierFirstComponentMatch "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.16 USAGE directoryOperation )",
-		subentryAttribute, 0, NULL, NULL, NULL,
+		subentryAttribute, SLAP_AT_HIDE, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_ditContentRules) },
 	{ "matchingRules", "( 2.5.21.4 NAME 'matchingRules' "
 			"DESC 'RFC2252: matching rules' "
@@ -389,13 +406,13 @@ static struct slap_schema_ad_map {
 			"DESC 'RFC2252: name forms ' "
 			"EQUALITY objectIdentifierFirstComponentMatch "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.35 USAGE directoryOperation )",
-		subentryAttribute, 0, NULL, NULL, NULL,
+		subentryAttribute, SLAP_AT_HIDE, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_nameForms) },
 	{ "matchingRuleUse", "( 2.5.21.8 NAME 'matchingRuleUse' "
 			"DESC 'RFC2252: matching rule uses' "
 			"EQUALITY objectIdentifierFirstComponentMatch "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.31 USAGE directoryOperation )",
-		subentryAttribute, 0, NULL, NULL, NULL,
+		subentryAttribute, SLAP_AT_HIDE, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_matchingRuleUse) },
 
 	{ "ldapSyntaxes", "( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes' "
@@ -411,7 +428,7 @@ static struct slap_schema_ad_map {
 			"DESC 'RFC2256: name of aliased object' "
 			"EQUALITY distinguishedNameMatch "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )",
-		aliasAttribute, 0, NULL, NULL, NULL,
+		aliasAttribute, SLAP_AT_FINAL, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_aliasedObjectName) },
 	{ "ref", "( 2.16.840.1.113730.3.1.34 NAME 'ref' "
 			"DESC 'namedref: subordinate referral URL' "
@@ -427,14 +444,14 @@ static struct slap_schema_ad_map {
 			"DESC 'OpenLDAP ACL entry pseudo-attribute' "
 			"SYNTAX 1.3.6.1.4.1.4203.1.1.1 "
 			"SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )",
-		NULL, 0, NULL, NULL, NULL,
+		NULL, SLAP_AT_HIDE, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_entry) },
 	{ "children", "( 1.3.6.1.4.1.4203.1.3.2 "
 			"NAME 'children' "
 			"DESC 'OpenLDAP ACL children pseudo-attribute' "
 			"SYNTAX 1.3.6.1.4.1.4203.1.1.1 "
 			"SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )",
-		NULL, 0, NULL, NULL, NULL,
+		NULL, SLAP_AT_HIDE, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_children) },
 
 #ifdef SLAPD_ACI_ENABLED
@@ -453,14 +470,14 @@ static struct slap_schema_ad_map {
 			"DESC 'RFC2256: common supertype of DN attributes' "
 			"EQUALITY distinguishedNameMatch "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
-		NULL, 0, NULL, NULL, NULL,
+		NULL, SLAP_AT_ABSTRACT, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_distinguishedName) },
 	{ "name", "( 2.5.4.41 NAME 'name' "
 			"DESC 'RFC2256: common supertype of name attributes' "
 			"EQUALITY caseIgnoreMatch "
 			"SUBSTR caseIgnoreSubstringsMatch "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )",
-		NULL, 0, NULL, NULL, NULL,
+		NULL, SLAP_AT_ABSTRACT, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_name) },
 	{ "cn", "( 2.5.4.3 NAME ( 'cn' 'commonName' ) "
 			"DESC 'RFC2256: common name(s) for which the entity is known by' "
@@ -471,7 +488,7 @@ static struct slap_schema_ad_map {
 			"DESC 'RFC2256/2307: password of user' "
 			"EQUALITY octetStringMatch "
 			"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} )",
-		NULL, 0, NULL, NULL, NULL,
+		NULL, SLAP_AT_FINAL, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_userPassword) },
 
 #ifdef SLAPD_AUTHPASSWD
@@ -480,7 +497,7 @@ static struct slap_schema_ad_map {
 			"DESC 'RFC3112: authentication password attribute' "
 			"EQUALITY 1.3.6.1.4.1.4203.1.2.2 "
 			"SYNTAX 1.3.6.1.4.1.4203.1.1.2 )",
-		NULL, 0,
+		NULL, SLAP_AT_FINAL,
 		NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_authPassword) },
 	{ "supportedAuthPasswordSchemes", "( 1.3.6.1.4.1.4203.1.3.3 "
@@ -494,7 +511,7 @@ static struct slap_schema_ad_map {
 #endif
 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
 	{ "krbName", NULL,
-		NULL, 0, NULL, NULL, NULL,
+		NULL, SLAP_AT_FINAL, NULL, NULL, NULL,
 		offsetof(struct slap_internal_schema, si_ad_krbName) },
 #endif
 
@@ -510,7 +527,8 @@ static AttributeType slap_at_undefined = {
 	NULL, /* subtypes */
 	NULL, NULL, NULL, NULL,	/* matching rules */
 	NULL, /* syntax (this may need to be defined) */
-	(AttributeTypeSchemaCheckFN *) 0, 0, /* schema check function/mask */
+	(AttributeTypeSchemaCheckFN *) 0, /* schema check function */
+	SLAP_AT_ABSTRACT|SLAP_AT_FINAL,	/* mask */
 	NULL, /* next */
 	NULL /* attribute description */
 	/* mutex (don't know how to initialize it :) */
@@ -663,7 +681,7 @@ slap_schema_load( void )
 				return LDAP_OTHER;
 			}
 
-			code = oc_add(oc,&err);
+			code = oc_add(oc,0,&err);
 			if ( code ) {
 				fprintf( stderr, "slap_schema_load: "
 					"%s: %s: \"%s\"\n",
diff --git a/servers/slapd/schemaparse.c b/servers/slapd/schemaparse.c
index 171bd3473e..10e92f827c 100644
--- a/servers/slapd/schemaparse.c
+++ b/servers/slapd/schemaparse.c
@@ -21,22 +21,23 @@ int	global_schemacheck = 1; /* schemacheck ON is default */
 static void		oc_usage(void); 
 static void		at_usage(void);
 
-static char *const err2text[SLAP_SCHERR_LAST+1] = {
-	"Success",
+static char *const err2text[] = {
 	"Out of memory",
 	"ObjectClass not found",
 	"ObjectClass inappropriate SUPerior",
+	"ObjectClass operational",
+	"Duplicate objectClass",
 	"AttributeType not found",
 	"AttributeType inappropriate USAGE",
-	"Duplicate objectClass",
+	"AttributeType inappropriate SUPerior",
+	"AttributeType SYNTAX or SUPerior required",
 	"Duplicate attributeType",
-	"Duplicate ldapSyntax",
-	"Duplicate matchingRule",
-	"OID or name required",
-	"SYNTAX or SUPerior required",
 	"MatchingRule not found",
+	"Duplicate matchingRule",
 	"Syntax not found",
 	"Syntax required",
+	"Duplicate ldapSyntax",
+	"OID or name required",
 	"Qualifier not supported",
 	"Invalid NAME",
 	"OID could not be expanded"
@@ -45,7 +46,7 @@ static char *const err2text[SLAP_SCHERR_LAST+1] = {
 char *
 scherr2str(int code)
 {
-	if ( code < 0 || code >= (int)(sizeof(err2text)/sizeof(char *)) ) {
+	if ( code < 0 || SLAP_SCHERR_LAST < code ) {
 		return "Unknown error";
 	} else {
 		return err2text[code];
@@ -118,7 +119,7 @@ parse_oc(
 		return 1;
 	}
 
-	code = oc_add(oc,&err);
+	code = oc_add(oc,1,&err);
 	if ( code ) {
 		fprintf( stderr, "%s: line %d: %s: \"%s\"\n",
 			 fname, lineno, scherr2str(code), err);
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
index 62ddc37bcc..1437f35cc4 100644
--- a/servers/slapd/slap.h
+++ b/servers/slapd/slap.h
@@ -218,24 +218,26 @@ typedef struct slap_ssf_set {
 /*
  * represents schema information for a database
  */
-#define SLAP_SCHERR_OUTOFMEM		1
-#define SLAP_SCHERR_CLASS_NOT_FOUND	2
-#define SLAP_SCHERR_CLASS_BAD_USAGE	3
-#define SLAP_SCHERR_ATTR_NOT_FOUND	4
-#define SLAP_SCHERR_ATTR_BAD_USAGE	5
-#define SLAP_SCHERR_DUP_CLASS		6
-#define SLAP_SCHERR_DUP_ATTR		7
-#define SLAP_SCHERR_DUP_SYNTAX		8
-#define SLAP_SCHERR_DUP_RULE		9
-#define SLAP_SCHERR_NO_NAME		10
-#define SLAP_SCHERR_ATTR_INCOMPLETE	11
-#define SLAP_SCHERR_MR_NOT_FOUND	12
-#define SLAP_SCHERR_SYN_NOT_FOUND	13
-#define SLAP_SCHERR_MR_INCOMPLETE	14
-#define SLAP_SCHERR_NOT_SUPPORTED	15
-#define SLAP_SCHERR_BAD_DESCR		16
-#define SLAP_SCHERR_OIDM			17
-#define SLAP_SCHERR_LAST			SLAP_SCHERR_OIDM
+#define SLAP_SCHERR_OUTOFMEM			1
+#define SLAP_SCHERR_CLASS_NOT_FOUND		2
+#define SLAP_SCHERR_CLASS_BAD_USAGE		3
+#define SLAP_SCHERR_CLASS_BAD_SUP		4
+#define SLAP_SCHERR_CLASS_DUP			5
+#define SLAP_SCHERR_ATTR_NOT_FOUND		6
+#define SLAP_SCHERR_ATTR_BAD_USAGE		7
+#define SLAP_SCHERR_ATTR_BAD_SUP		8
+#define SLAP_SCHERR_ATTR_INCOMPLETE		9
+#define SLAP_SCHERR_ATTR_DUP			10
+#define SLAP_SCHERR_MR_NOT_FOUND		11
+#define SLAP_SCHERR_MR_INCOMPLETE		12
+#define SLAP_SCHERR_MR_DUP				13
+#define SLAP_SCHERR_SYN_NOT_FOUND		14
+#define SLAP_SCHERR_SYN_DUP				15
+#define SLAP_SCHERR_NO_NAME				16
+#define SLAP_SCHERR_NOT_SUPPORTED		17
+#define SLAP_SCHERR_BAD_DESCR			18
+#define SLAP_SCHERR_OIDM				19
+#define SLAP_SCHERR_LAST				SLAP_SCHERR_OIDM
 
 typedef union slap_sockaddr {
 	struct sockaddr sa_addr;
@@ -431,6 +433,11 @@ typedef struct slap_attribute_type {
 	Syntax					*sat_syntax;
 
 	AttributeTypeSchemaCheckFN	*sat_check;
+
+#define SLAP_AT_NONE		0x0000U
+#define SLAP_AT_ABSTRACT	0x0100U /* cannot be instantiated */
+#define SLAP_AT_FINAL		0x0200U /* cannot be subtyped */
+#define SLAP_AT_HIDE		0x8000U /* hide attribute */
 	slap_mask_t					sat_flags;
 
 	struct slap_attribute_type	*sat_next;
@@ -489,13 +496,15 @@ typedef struct slap_object_class {
 	struct slap_object_class	*soc_next;
 } ObjectClass;
 
-#define	SLAP_OC_ALIAS		0x01
-#define	SLAP_OC_REFERRAL	0x02
-#define	SLAP_OC_SUBENTRY	0x04
-#define	SLAP_OC_DYNAMICOBJECT	0x08
-#define	SLAP_OC_COLLECTIVEATTRIBUTESUBENTRY	0x10
-#define	SLAP_OC__MASK		0x1F
-#define	SLAP_OC__END		0x20
+#define	SLAP_OC_ALIAS		0x0001
+#define	SLAP_OC_REFERRAL	0x0002
+#define	SLAP_OC_SUBENTRY	0x0004
+#define	SLAP_OC_DYNAMICOBJECT	0x0008
+#define	SLAP_OC_COLLECTIVEATTRIBUTESUBENTRY	0x0010
+#define	SLAP_OC__MASK		0x001F
+#define	SLAP_OC__END		0x0020
+#define SLAP_OC_OPERATIONAL	0x4000
+#define SLAP_OC_HIDE		0x8000
 
 #ifdef LDAP_EXTENDED_SCHEMA
 /*
diff --git a/servers/slapd/syntax.c b/servers/slapd/syntax.c
new file mode 100644
index 0000000000..eea2e1b110
--- /dev/null
+++ b/servers/slapd/syntax.c
@@ -0,0 +1,240 @@
+/* syntax.c - routines to manage syntax definitions */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "ldap_pvt.h"
+
+
+struct sindexrec {
+	char		*sir_name;
+	Syntax		*sir_syn;
+};
+
+static Avlnode	*syn_index = NULL;
+static Syntax *syn_list = NULL;
+
+static int
+syn_index_cmp(
+    struct sindexrec	*sir1,
+    struct sindexrec	*sir2
+)
+{
+	return (strcmp( sir1->sir_name, sir2->sir_name ));
+}
+
+static int
+syn_index_name_cmp(
+    const char		*name,
+    struct sindexrec	*sir
+)
+{
+	return (strcmp( name, sir->sir_name ));
+}
+
+Syntax *
+syn_find( const char *synname )
+{
+	struct sindexrec	*sir = NULL;
+
+	if ( (sir = (struct sindexrec *) avl_find( syn_index, synname,
+	    (AVL_CMP) syn_index_name_cmp )) != NULL ) {
+		return( sir->sir_syn );
+	}
+	return( NULL );
+}
+
+Syntax *
+syn_find_desc( const char *syndesc, int *len )
+{
+	Syntax		*synp;
+
+	for (synp = syn_list; synp; synp = synp->ssyn_next)
+		if ((*len = dscompare( synp->ssyn_syn.syn_desc, syndesc, '{')))
+			return synp;
+	return( NULL );
+}
+
+void
+syn_destroy( void )
+{
+	Syntax *s, *n;
+
+	avl_free(syn_index, ldap_memfree);
+	for (s=syn_list; s; s=n) {
+		n = s->ssyn_next;
+		ldap_syntax_free((LDAPSyntax *)s);
+	}
+}
+
+static int
+syn_insert(
+    Syntax		*ssyn,
+    const char		**err
+)
+{
+	Syntax		**synp;
+	struct sindexrec	*sir;
+
+	synp = &syn_list;
+	while ( *synp != NULL ) {
+		synp = &(*synp)->ssyn_next;
+	}
+	*synp = ssyn;
+
+	if ( ssyn->ssyn_oid ) {
+		sir = (struct sindexrec *)
+			ch_calloc( 1, sizeof(struct sindexrec) );
+		sir->sir_name = ssyn->ssyn_oid;
+		sir->sir_syn = ssyn;
+		if ( avl_insert( &syn_index, (caddr_t) sir,
+				 (AVL_CMP) syn_index_cmp,
+				 (AVL_DUP) avl_dup_error ) ) {
+			*err = ssyn->ssyn_oid;
+			ldap_memfree(sir);
+			return SLAP_SCHERR_SYN_DUP;
+		}
+		/* FIX: temporal consistency check */
+		syn_find(sir->sir_name);
+	}
+	return 0;
+}
+
+int
+syn_add(
+    LDAPSyntax		*syn,
+	unsigned flags,
+    slap_syntax_validate_func	*validate,
+    slap_syntax_transform_func	*normalize,
+    slap_syntax_transform_func	*pretty,
+#ifdef SLAPD_BINARY_CONVERSION
+    slap_syntax_transform_func	*ber2str,
+    slap_syntax_transform_func	*str2ber,
+#endif
+    const char		**err
+)
+{
+	Syntax		*ssyn;
+	int		code;
+
+	ssyn = (Syntax *) ch_calloc( 1, sizeof(Syntax) );
+
+	AC_MEMCPY( &ssyn->ssyn_syn, syn, sizeof(LDAPSyntax) );
+
+	ssyn->ssyn_next = NULL;
+
+	ssyn->ssyn_oidlen = strlen(syn->syn_oid);
+	ssyn->ssyn_flags = flags;
+	ssyn->ssyn_validate = validate;
+	ssyn->ssyn_normalize = normalize;
+	ssyn->ssyn_pretty = pretty;
+
+#ifdef SLAPD_BINARY_CONVERSION
+	ssyn->ssyn_ber2str = ber2str;
+	ssyn->ssyn_str2ber = str2ber;
+#endif
+
+	code = syn_insert(ssyn, err);
+	return code;
+}
+
+int
+register_syntax(
+	const char * desc,
+	unsigned flags,
+	slap_syntax_validate_func *validate,
+	slap_syntax_transform_func *normalize,
+	slap_syntax_transform_func *pretty )
+{
+	LDAPSyntax	*syn;
+	int		code;
+	const char	*err;
+
+	syn = ldap_str2syntax( desc, &code, &err, LDAP_SCHEMA_ALLOW_ALL);
+	if ( !syn ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG(( "schema", LDAP_LEVEL_ERR,
+			   "register_syntax: Error - %s before %s in %s.\n",
+			   ldap_scherr2str(code), err, desc ));
+#else
+		Debug( LDAP_DEBUG_ANY, "Error in register_syntax: %s before %s in %s\n",
+		    ldap_scherr2str(code), err, desc );
+#endif
+
+		return( -1 );
+	}
+
+	code = syn_add( syn, flags, validate, normalize, pretty, &err );
+
+	ldap_memfree( syn );
+
+	if ( code ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG(( "schema", LDAP_LEVEL_ERR,
+			   "register_syntax: Error - %s %s in %s\n",
+			   scherr2str(code), err, desc ));
+#else
+		Debug( LDAP_DEBUG_ANY, "Error in register_syntax: %s %s in %s\n",
+		    scherr2str(code), err, desc );
+#endif
+
+		return( -1 );
+	}
+
+	return( 0 );
+}
+
+#if defined( SLAPD_SCHEMA_DN )
+
+int
+syn_schema_info( Entry *e )
+{
+	struct berval	vals[2];
+	Syntax		*syn;
+
+	AttributeDescription *ad_ldapSyntaxes = slap_schema.si_ad_ldapSyntaxes;
+
+	vals[1].bv_val = NULL;
+
+	for ( syn = syn_list; syn; syn = syn->ssyn_next ) {
+		if ( ! syn->ssyn_validate ) {
+			/* skip syntaxes without validators */
+			continue;
+		}
+		if ( syn->ssyn_flags & SLAP_SYNTAX_HIDE ) {
+			/* hide syntaxes */
+			continue;
+		}
+
+		if ( ldap_syntax2bv( &syn->ssyn_syn, vals ) == NULL ) {
+			return -1;
+		}
+#if 0
+#ifdef NEW_LOGGING
+		LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
+			   "syn_schema_info: Merging syn [%ld] %s\n",
+			   (long)vals[0].bv_len, vals[0].bv_val ));
+#else
+		Debug( LDAP_DEBUG_TRACE, "Merging syn [%ld] %s\n",
+	       (long) vals[0].bv_len, vals[0].bv_val, 0 );
+#endif
+
+#endif
+		attr_merge( e, ad_ldapSyntaxes, vals );
+		ldap_memfree( vals[0].bv_val );
+	}
+	return 0;
+}
+
+#endif
-- 
GitLab