From 25d46e7578842557738d3f48d7f102be11a09ac6 Mon Sep 17 00:00:00 2001
From: Pierangelo Masarati <ando@openldap.org>
Date: Wed, 22 Aug 2007 22:35:14 +0000
Subject: [PATCH] rework controls API

---
 doc/man/man3/ldap_controls.3       | 61 ++++++++++++++----
 doc/man/man3/ldap_controls.3.links |  6 +-
 include/ldap.h                     | 28 ++++++++-
 libraries/libldap/controls.c       | 99 +++++++++++++++++++++++++-----
 libraries/libldap/pagectrl.c       | 17 ++---
 libraries/libldap/ppolicy.c        | 11 ++--
 libraries/libldap/sortctrl.c       | 20 +++---
 libraries/libldap/stctrl.c         | 10 +--
 libraries/libldap/vlvctrl.c        | 14 +----
 servers/slapd/syncrepl.c           | 11 +++-
 10 files changed, 194 insertions(+), 83 deletions(-)

diff --git a/doc/man/man3/ldap_controls.3 b/doc/man/man3/ldap_controls.3
index 7cb7d21ddd..413ce0817e 100644
--- a/doc/man/man3/ldap_controls.3
+++ b/doc/man/man3/ldap_controls.3
@@ -3,45 +3,80 @@
 .\" Copyright 1998-2007 The OpenLDAP Foundation All Rights Reserved.
 .\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
 .SH NAME
-ldap_create_control, ldap_find_control, ldap_control_free, ldap_controls_free \- LDAP control manipulation routines
+ldap_control_create, ldap_control_find, ldap_control_dup,
+ldap_controls_dup, ldap_control_free, ldap_controls_free
+\- LDAP control manipulation routines
 .SH LIBRARY
 OpenLDAP LDAP (libldap, -lldap)
 .SH SYNOPSIS
 .B #include <ldap.h>
 .LP
-.BI "int ldap_create_control(LDAP_CONST char *" OID ", BerElement *" ber ", int " iscritical ", LDAPControl **" ctrlp ");"
+.BI "int ldap_control_create(const char *" oid ", int " iscritical ", struct berval *" value ", int " dupval ", LDAPControl **" ctrlp ");"
 .LP
-.BI "LDAPControl *ldap_find_control(LDAP_CONST char *" OID ", LDAPControl **" ctrls ");"
+.BI "LDAPControl *ldap_control_find( const char *" oid ", LDAPControl **" ctrls ", LDAPControl ***" nextctrlp ");"
+.LP
+.BI "LDAPControl *ldap_control_dup(LDAPControl *" ctrl ");"
+.LP
+.BI "LDAPControl **ldap_controls_dup(LDAPControl **" ctrls ");"
 .LP
 .BI "void ldap_control_free(LDAPControl *" ctrl ");"
 .LP
 .BI "void ldap_controls_free(LDAPControl **" ctrls ");"
 .SH DESCRIPTION
 These routines are used to manipulate structures used for LDAP controls.
-.BR ldap_create_control ()
+
+.BR ldap_control_create ()
 creates a control with the specified
 .I OID
 using the contents of the
-.I ber
-parameter for the control value, if any. The
+.I value
+parameter for the control value, if any.  The content of
+.I value 
+is duplicated if
+.I dupval
+is non-zero.  The
 .I iscritical
-parameter should be non-zero for a critical control. The created control
+parameter must be non-zero for a critical control. The created control
 is returned in the
 .I ctrlp
-parameter. The routine returns
+parameter.  The routine returns
 .B LDAP_SUCCESS
 on success or some other error code on failure.
-.BR ldap_find_control ()
-searches the
+The content of
+.IR value ,
+for supported control types, can be prepared using helpers provided
+by this implementation of libldap, usually in the form
+.BR "ldap_create_<control name>_control_value" ().
+Otherwise, it can be BER-encoded using the functionalities of liblber.
+
+.BR ldap_control_find ()
+searches the NULL-terminated
 .I ctrls
 array for a control whose OID matches the
-.I OID
-parameter. The routine returns a pointer to the control if found,
+.I oid
+parameter.  The routine returns a pointer to the control if found,
 NULL otherwise.
+If the parameter
+.I nextctrlp
+is not NULL, on return it will point to the next control
+in the array, and can be passed to the
+.BR ldap_control_find ()
+routine for subsequent calls, to find further occurrences of the same 
+control type.
+The use of this function is discouraged; the recommended way of handling
+controls in responses consists in going through the array of controls,
+dealing with each of them in the returned order, since it could matter.
+
+.BR ldap_control_dup ()
+duplicates an individual control structure, and
+.BR ldap_controls_dup ()
+duplicates a NULL-terminated array of controls.
+
 .BR ldap_control_free ()
 frees an individual control structure, and
 .BR ldap_controls_free ()
-frees an array of controls.
+frees a NULL-terminated array of controls.
+
 .SH SEE ALSO
 .BR ldap (3),
 .BR ldap_error (3)
diff --git a/doc/man/man3/ldap_controls.3.links b/doc/man/man3/ldap_controls.3.links
index 03cd358cc8..6c5248f641 100644
--- a/doc/man/man3/ldap_controls.3.links
+++ b/doc/man/man3/ldap_controls.3.links
@@ -1,4 +1,6 @@
-ldap_create_control.3
-ldap_find_control.3
+ldap_control_create.3
+ldap_control_find.3
+ldap_control_dup.3
+ldap_controls_dup.3
 ldap_control_free.3
 ldap_controls_free.3
diff --git a/include/ldap.h b/include/ldap.h
index 2f217523d9..2e55abd007 100644
--- a/include/ldap.h
+++ b/include/ldap.h
@@ -941,17 +941,33 @@ ldap_set_urllist_proc LDAP_P((
 /*
  * in controls.c:
  */
+#if LDAP_DEPRECATED	
 LDAP_F( int )
-ldap_create_control LDAP_P((
+ldap_create_control LDAP_P((	/* deprecated, use ldap_control_create */
 	LDAP_CONST char *requestOID,
 	BerElement *ber,
 	int iscritical,
 	LDAPControl **ctrlp ));
 
 LDAP_F( LDAPControl * )
-ldap_find_control LDAP_P((
+ldap_find_control LDAP_P((	/* deprecated, use ldap_control_find */
 	LDAP_CONST char *oid,
 	LDAPControl **ctrls ));
+#endif
+
+LDAP_F( int )
+ldap_control_create LDAP_P((
+	LDAP_CONST char *requestOID,
+	int iscritical,
+	struct berval *value,
+	int dupval,
+	LDAPControl **ctrlp ));
+
+LDAP_F( LDAPControl * )
+ldap_control_find LDAP_P((
+	LDAP_CONST char *oid,
+	LDAPControl **ctrls,
+	LDAPControl ***nextctrlp ));
 
 LDAP_F( void )
 ldap_control_free LDAP_P((
@@ -961,6 +977,14 @@ LDAP_F( void )
 ldap_controls_free LDAP_P((
 	LDAPControl **ctrls ));
 
+LDAP_F( LDAPControl ** )
+ldap_controls_dup LDAP_P((
+	LDAPControl *LDAP_CONST *controls ));
+
+LDAP_F( LDAPControl * )
+ldap_control_dup LDAP_P((
+	LDAP_CONST LDAPControl *c ));
+
 /*
  * in dnssrv.c:
  */
diff --git a/libraries/libldap/controls.c b/libraries/libldap/controls.c
index c9847a3b7d..ab5ae46da9 100644
--- a/libraries/libldap/controls.c
+++ b/libraries/libldap/controls.c
@@ -382,7 +382,9 @@ ldap_control_dup( const LDAPControl *c )
 	return new;
 }
 
-
+/*
+ * Find a LDAPControl - deprecated
+ */
 LDAPControl *
 ldap_find_control(
 	LDAP_CONST char *oid,
@@ -402,21 +404,38 @@ ldap_find_control(
 }
 
 /*
-   ldap_create_control
-   
-   Internal function to create an LDAP control from the encoded BerElement.
-
-   requestOID  (IN) The OID to use in creating the control.
-   
-   ber         (IN) The encoded BerElement to use in creating the control.
-   
-   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.
----*/
+ * Find a LDAPControl
+ */
+LDAPControl *
+ldap_control_find(
+	LDAP_CONST char *oid,
+	LDAPControl **ctrls,
+	LDAPControl ***nextctrlp )
+{
+	if ( oid == NULL || ctrls == NULL || *ctrls == NULL ) {
+		return NULL;
+	}
+
+	for( ; *ctrls != NULL; ctrls++ ) {
+		if( strcmp( (*ctrls)->ldctl_oid, oid ) == 0 ) {
+			if ( nextctrlp != NULL ) {
+				*nextctrlp = ctrls + 1;
+			}
+
+			return *ctrls;
+		}
+	}
+
+	if ( nextctrlp != NULL ) {
+		*nextctrlp = NULL;
+	}
+
+	return NULL;
+}
 
+/*
+ * Create a LDAPControl, optionally from ber - deprecated
+ */
 int
 ldap_create_control(
 	LDAP_CONST char *requestOID,
@@ -434,7 +453,7 @@ ldap_create_control(
 		return LDAP_NO_MEMORY;
 	}
 
-    BER_BVZERO(&ctrl->ldctl_value);
+	BER_BVZERO(&ctrl->ldctl_value);
 	if ( ber && ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 )) {
 		LDAP_FREE( ctrl );
 		return LDAP_NO_MEMORY;
@@ -452,6 +471,54 @@ ldap_create_control(
 	return LDAP_SUCCESS;
 }
 
+/*
+ * Create a LDAPControl, optionally from value
+ */
+int
+ldap_control_create(
+	LDAP_CONST char *requestOID,
+	int iscritical,
+	struct berval *value,
+	int dupval,
+	LDAPControl **ctrlp )
+{
+	LDAPControl *ctrl;
+
+	assert( requestOID != NULL );
+	assert( ctrlp != NULL );
+
+	ctrl = (LDAPControl *) LDAP_CALLOC( sizeof(LDAPControl), 1 );
+	if ( ctrl == NULL ) {
+		return LDAP_NO_MEMORY;
+	}
+
+	ctrl->ldctl_iscritical = iscritical;
+	if ( requestOID != NULL ) {
+		ctrl->ldctl_oid = LDAP_STRDUP( requestOID );
+		if ( ctrl->ldctl_oid == NULL ) {
+			ldap_control_free( ctrl );
+			return LDAP_NO_MEMORY;
+		}
+	}
+
+	if ( value && !BER_BVISNULL( value ) ) {
+		if ( dupval ) {
+			ber_dupbv( &ctrl->ldctl_value, value );
+			if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+				ldap_control_free( ctrl );
+				return LDAP_NO_MEMORY;
+			}
+
+		} else {
+			ctrl->ldctl_value = *value;
+		}
+	}
+
+	*ctrlp = ctrl;
+
+	return LDAP_SUCCESS;
+}
+
 /*
  * check for critical client controls and bitch if present
  * if we ever support critical controls, we'll have to
diff --git a/libraries/libldap/pagectrl.c b/libraries/libldap/pagectrl.c
index df4978705d..93222406b1 100644
--- a/libraries/libldap/pagectrl.c
+++ b/libraries/libldap/pagectrl.c
@@ -139,7 +139,6 @@ ldap_create_page_control(
 	LDAPControl	**ctrlp )
 {
 	struct berval	value;
-	BerElement	*ber;
 
 	if ( ctrlp == NULL ) {
 		ld->ld_errno = LDAP_PARAM_ERROR;
@@ -149,19 +148,11 @@ ldap_create_page_control(
 	ld->ld_errno = ldap_create_page_control_value( ld,
 		pagesize, cookie, &value );
 	if ( ld->ld_errno == LDAP_SUCCESS ) {
-		if ((ber = ldap_alloc_ber_with_options(ld)) == NULL) {
-			ld->ld_errno = LDAP_NO_MEMORY;
-			return LDAP_NO_MEMORY;
-		}
-
-		ld->ld_errno = ldap_create_control( LDAP_CONTROL_PAGEDRESULTS,
-			ber, iscritical, ctrlp );
-		if ( ld->ld_errno == LDAP_SUCCESS ) {
-			(*ctrlp)->ldctl_value = value;
-		} else {
+		ld->ld_errno = ldap_control_create( LDAP_CONTROL_PAGEDRESULTS,
+			iscritical, &value, 0, ctrlp );
+		if ( ld->ld_errno != LDAP_SUCCESS ) {
 			LDAP_FREE( value.bv_val );
 		}
-		ber_free(ber, 1);
 	}
 
 	return ld->ld_errno;
@@ -256,7 +247,7 @@ ldap_parse_page_control(
 		return ld->ld_errno;
 	}
 
-	c = ldap_find_control( LDAP_CONTROL_PAGEDRESULTS, ctrls );
+	c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL );
 	if ( c == NULL ) {
 		/* No page control was found. */
 		ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
diff --git a/libraries/libldap/ppolicy.c b/libraries/libldap/ppolicy.c
index b82208fe76..ad7153406f 100644
--- a/libraries/libldap/ppolicy.c
+++ b/libraries/libldap/ppolicy.c
@@ -65,10 +65,10 @@ ldap_create_passwordpolicy_control( LDAP *ld,
 	assert( LDAP_VALID( ld ) );
 	assert( ctrlp != NULL );
 
-	ld->ld_errno = ldap_create_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
-		NULL, 0, ctrlp);
+	ld->ld_errno = ldap_control_create( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+		0, NULL, 0, ctrlp );
 
-	return(ld->ld_errno);
+	return ld->ld_errno;
 }
 
 
@@ -80,8 +80,9 @@ ldap_create_passwordpolicy_control( LDAP *ld,
    ld           (IN)   An LDAP session handle.
    
    ctrl         (IN)   The address of an
-					   LDAPControl structure, typically obtained 
-					   by a call to ldap_find_control().
+					   LDAPControl structure, either obtained 
+					   by running thorugh the list of response controls or
+					   by a call to ldap_control_find().
 
    exptimep     (OUT)  This result parameter is filled in with the number of seconds before
                                            the password will expire, if expiration is imminent
diff --git a/libraries/libldap/sortctrl.c b/libraries/libldap/sortctrl.c
index 0200e01dac..05c66eec09 100644
--- a/libraries/libldap/sortctrl.c
+++ b/libraries/libldap/sortctrl.c
@@ -408,12 +408,14 @@ ldap_create_sort_control(
 	LDAPControl **ctrlp )
 {
 	struct berval	value;
-	BerElement	*ber;
 
 	assert( ld != NULL );
 	assert( LDAP_VALID( ld ) );
 
-	if ( ld == NULL ) return LDAP_PARAM_ERROR;
+	if ( ld == NULL ) {
+		return LDAP_PARAM_ERROR;
+	}
+
 	if ( ctrlp == NULL ) {
 		ld->ld_errno = LDAP_PARAM_ERROR;
 		return ld->ld_errno;
@@ -421,19 +423,11 @@ ldap_create_sort_control(
 
 	ld->ld_errno = ldap_create_sort_control_value( ld, keyList, &value );
 	if ( ld->ld_errno == LDAP_SUCCESS ) {
-		if ((ber = ldap_alloc_ber_with_options(ld)) == NULL) {
-			ld->ld_errno = LDAP_NO_MEMORY;
-			return LDAP_NO_MEMORY;
-		}
-	
-		ld->ld_errno = ldap_create_control( LDAP_CONTROL_SORTREQUEST,
-			ber, isCritical, ctrlp );
-		if ( ld->ld_errno == LDAP_SUCCESS ) {
-			(*ctrlp)->ldctl_value = value;
-		} else {
+		ld->ld_errno = ldap_control_create( LDAP_CONTROL_SORTREQUEST,
+			isCritical, &value, 0, ctrlp );
+		if ( ld->ld_errno != LDAP_SUCCESS ) {
 			LDAP_FREE( value.bv_val );
 		}
-		ber_free(ber, 1);
 	}
 
 	return ld->ld_errno;
diff --git a/libraries/libldap/stctrl.c b/libraries/libldap/stctrl.c
index bfec5c44e7..4dc197cb6c 100644
--- a/libraries/libldap/stctrl.c
+++ b/libraries/libldap/stctrl.c
@@ -137,7 +137,6 @@ ldap_create_session_tracking_control(
 	LDAPControl	**ctrlp )
 {
 	struct berval	value;
-	BerElement	*ber;
 
 	if ( ctrlp == NULL ) {
 		ld->ld_errno = LDAP_PARAM_ERROR;
@@ -148,12 +147,9 @@ ldap_create_session_tracking_control(
 		sessionSourceIp, sessionSourceName, formatOID,
 		sessionTrackingIdentifier, &value );
 	if ( ld->ld_errno == LDAP_SUCCESS ) {
-		ld->ld_errno = ldap_create_control( LDAP_CONTROL_X_SESSION_TRACKING,
-			NULL, 0, ctrlp );
-		if ( ld->ld_errno == LDAP_SUCCESS ) {
-			(*ctrlp)->ldctl_value = value;
-
-		} else {
+		ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_SESSION_TRACKING,
+			0, &value, 0, ctrlp );
+		if ( ld->ld_errno != LDAP_SUCCESS ) {
 			LDAP_FREE( value.bv_val );
 		}
 	}
diff --git a/libraries/libldap/vlvctrl.c b/libraries/libldap/vlvctrl.c
index f301a2e641..f0c4cb446e 100644
--- a/libraries/libldap/vlvctrl.c
+++ b/libraries/libldap/vlvctrl.c
@@ -210,7 +210,6 @@ ldap_create_vlv_control(
 	LDAPControl **ctrlp )
 {
 	struct berval	value;
-	BerElement	*ber;
 
 	if ( ctrlp == NULL ) {
 		ld->ld_errno = LDAP_PARAM_ERROR;
@@ -219,19 +218,12 @@ ldap_create_vlv_control(
 
 	ld->ld_errno = ldap_create_vlv_control_value( ld, vlvinfop, &value );
 	if ( ld->ld_errno == LDAP_SUCCESS ) {
-		if ((ber = ldap_alloc_ber_with_options(ld)) == NULL) {
-			ld->ld_errno = LDAP_NO_MEMORY;
-			return LDAP_NO_MEMORY;
-		}
 
-		ld->ld_errno = ldap_create_control( LDAP_CONTROL_VLVREQUEST,
-			ber, 1, ctrlp );
-		if ( ld->ld_errno == LDAP_SUCCESS ) {
-			(*ctrlp)->ldctl_value = value;
-		} else {
+		ld->ld_errno = ldap_control_create( LDAP_CONTROL_VLVREQUEST,
+			1, &value, 0, ctrlp );
+		if ( ld->ld_errno != LDAP_SUCCESS ) {
 			LDAP_FREE( value.bv_val );
 		}
-		ber_free(ber, 1);
 	}
 
 	return ld->ld_errno;
diff --git a/servers/slapd/syncrepl.c b/servers/slapd/syncrepl.c
index f4a9c39531..cad1a1a218 100644
--- a/servers/slapd/syncrepl.c
+++ b/servers/slapd/syncrepl.c
@@ -742,10 +742,19 @@ do_syncrep2(
 				/* we can't work without the control */
 				rctrlp = NULL;
 				if ( rctrls ) {
+					LDAPControl **next;
 					/* NOTE: make sure we use the right one;
 					 * a better approach would be to run thru
 					 * the whole list and take care of all */
-					rctrlp = ldap_find_control( LDAP_CONTROL_SYNC_STATE, rctrls );
+					rctrlp = ldap_control_find( LDAP_CONTROL_SYNC_STATE, rctrls, &next );
+					if ( next && ldap_control_find( LDAP_CONTROL_SYNC_STATE, next, NULL ) )
+					{
+						Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+							"got search entry with multiple "
+							"Sync State control\n", si->si_ridtxt, 0, 0 );
+						rc = -1;
+						goto done;
+					}
 				}
 				if ( rctrlp == NULL ) {
 					Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
-- 
GitLab