]> git.sur5r.net Git - openldap/commitdiff
Add VLV/Sort controls (from Novell)
authorKurt Zeilenga <kurt@openldap.org>
Wed, 7 Jun 2000 23:43:56 +0000 (23:43 +0000)
committerKurt Zeilenga <kurt@openldap.org>
Wed, 7 Jun 2000 23:43:56 +0000 (23:43 +0000)
libraries/libldap/sortctrl.c [new file with mode: 0644]
libraries/libldap/vlvctrl.c [new file with mode: 0644]

diff --git a/libraries/libldap/sortctrl.c b/libraries/libldap/sortctrl.c
new file mode 100644 (file)
index 0000000..3f04a20
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * Copyright 1998-2000 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 <ac/ctype.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 (isspace(*p))              /* Skip leading whitespace */
+                       p++;
+
+               if (*p == '\0')                 /* End of string? */
+                       return count;
+
+               count++;                                /* Found start of a key */
+
+               while (!isspace(*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 (isspace(*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.                      
+   
+   ---------------------------------------------------------------------------*/
+
+LIBLDAP_F(int)
+ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString )
+{
+       int         numKeys, rc, i;
+       char        *nextKey;
+       LDAPSortKey **keyList = NULL;
+
+       if (( sortKeyList == NULL ) || ( keyString == NULL )) {
+               return LDAP_PARAM_ERROR;
+       }
+       *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.
+   ---------------------------------------------------------------------------*/
+
+LIBLDAP_F(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 }
+   
+   ---------------------------------------------------------------------------*/
+
+LIBLDAP_F( 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, /*{*/ "}");
+               if( tag == LBER_ERROR ) goto exit;
+       }
+
+       tag = ber_printf(ber, /*{*/ "}");
+       if( tag == LBER_ERROR ) goto exit;
+
+       ld->ld_errno = ldap_int_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 }
+   ---------------------------------------------------------------------------*/
+
+LIBLDAP_F( 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/vlvctrl.c b/libraries/libldap/vlvctrl.c
new file mode 100644 (file)
index 0000000..04b5526
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright 1998-2000 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 <ac/ctype.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.
+
+ ---*/
+
+LIBLDAP_F( int )
+ldap_create_vlv_control( LDAP *ld,
+                                                LDAPVLVInfo *vlvinfop,
+                                                LDAPControl **ctrlp )
+{
+       ber_tag_t tag;
+       BerElement *ber;
+
+       if ( (ld==NULL) || (vlvinfop==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(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{ii}",
+                       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, /*{*/ "}"); 
+       if( tag == LBER_ERROR ) goto exit;
+
+       ld->ld_errno = ldap_int_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 }
+   
+---*/
+
+LIBLDAP_F( 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;
+
+       if (contextp) {
+               *contextp = NULL;        /* Make sure we return a NULL if error occurs. */
+       }
+
+       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);
+       }
+
+       /* 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);
+}