+/* search.c */
/* $OpenLDAP$ */
-/*
- * Copyright 1998-2000 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.
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2004 The OpenLDAP Foundation.
+ * All rights reserved.
*
- * search.c
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* Portions Copyright (C) The Internet Society (1997).
+ * ASN.1 fragments are from RFC 2251; see RFC for full legal notices.
*/
#include "portable.h"
#include "ldap-int.h"
+static int put_simple_vrFilter LDAP_P((
+ BerElement *ber,
+ char *str ));
+
+static int put_vrFilter_list LDAP_P((
+ BerElement *ber,
+ char *str ));
+
static char *put_complex_filter LDAP_P((
BerElement *ber,
char *str,
return 0;
options:
- if( !LDAP_LDH( str[i] )) {
+ if( !LDAP_LDH( str[0] )) {
return 0;
}
for( i=1; str[i]; i++ ) {
if ( balance ) s++;
}
- return( *s ? s : NULL );
+ return *s ? s : NULL;
}
static int hex2value( int c )
case '*': /* found wildcard */
return (char *) s;
+ case '(':
+ case ')':
+ return NULL;
+
case '\\':
if( s[1] == '\0' ) return NULL;
return NULL;
/* allow RFC 1960 escapes */
- case '\\':
case '*':
case '(':
case ')':
+ case '\\':
s++;
}
}
for( r=v=0; fval[v] != '\0'; v++ ) {
switch( fval[v] ) {
+ case '(':
+ case ')':
+ case '*':
+ return -1;
+
case '\\':
/* escape */
v++;
*/
/* put explicit tag */
- if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 )
- return( NULL );
+ if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) {
+ return NULL;
+ }
str++;
- if ( (next = find_right_paren( str )) == NULL )
- return( NULL );
+ if ( (next = find_right_paren( str )) == NULL ) {
+ return NULL;
+ }
*next = '\0';
- if ( put_filter_list( ber, str, tag ) == -1 )
- return( NULL );
+ if ( put_filter_list( ber, str, tag ) == -1 ) {
+ return NULL;
+ }
/* close the '(' */
*next++ = ')';
/* flush explicit tagged thang */
- if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 )
- return( NULL );
+ if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
+ return NULL;
+ }
- return( next );
+ return next;
}
int
-ldap_int_put_filter( BerElement *ber, char *str )
+ldap_pvt_put_filter( BerElement *ber, const char *str_in )
{
+ int rc;
+ char *freeme;
+ char *str;
char *next;
int parens, balance, escape;
* lessOrEqual [6] AttributeValueAssertion,
* present [7] AttributeType,
* approxMatch [8] AttributeValueAssertion,
- * extensibleMatch [9] MatchingRuleAssertion -- LDAPv3
+ * extensibleMatch [9] MatchingRuleAssertion -- LDAPv3
* }
*
* SubstringFilter ::= SEQUENCE {
* }
* }
*
- * MatchingRuleAssertion ::= SEQUENCE { -- LDAPv3
- * matchingRule [1] MatchingRuleId OPTIONAL,
- * type [2] AttributeDescription OPTIONAL,
- * matchValue [3] AssertionValue,
- * dnAttributes [4] BOOLEAN DEFAULT FALSE }
+ * MatchingRuleAssertion ::= SEQUENCE { -- LDAPv3
+ * matchingRule [1] MatchingRuleId OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue,
+ * dnAttributes [4] BOOLEAN DEFAULT FALSE }
*
* Note: tags in a choice are always explicit
*/
- Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str, 0, 0 );
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, ARGS, "ldap_pvt_put_filter: \"%s\"\n", str_in,0,0 );
+#else
+ Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 );
+#endif
+
+ freeme = LDAP_STRDUP( str_in );
+ if( freeme == NULL ) return LDAP_NO_MEMORY;
+ str = freeme;
parens = 0;
while ( *str ) {
switch ( *str ) {
case '&':
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: AND\n", 0,0,0 );
+#else
Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
0, 0, 0 );
+#endif
str = put_complex_filter( ber, str,
LDAP_FILTER_AND, 0 );
- if( str == NULL ) return( -1 );
+ if( str == NULL ) {
+ rc = -1;
+ goto done;
+ }
parens--;
break;
case '|':
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: OR\n", 0,0,0 );
+#else
Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
0, 0, 0 );
+#endif
str = put_complex_filter( ber, str,
LDAP_FILTER_OR, 0 );
- if( str == NULL ) return( -1 );
+ if( str == NULL ) {
+ rc = -1;
+ goto done;
+ }
parens--;
break;
case '!':
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: NOT\n", 0,0,0 );
+#else
Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
0, 0, 0 );
+#endif
str = put_complex_filter( ber, str,
LDAP_FILTER_NOT, 0 );
- if( str == NULL ) return( -1 );
+ if( str == NULL ) {
+ rc = -1;
+ goto done;
+ }
parens--;
break;
default:
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: simple\n", 0,0,0);
+#else
Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
0, 0, 0 );
+#endif
balance = 1;
escape = 0;
next = str;
+
while ( *next && balance ) {
if ( escape == 0 ) {
- if ( *next == '(' )
+ if ( *next == '(' ) {
balance++;
- else if ( *next == ')' )
+ } else if ( *next == ')' ) {
balance--;
+ }
}
- if ( *next == '\\' && ! escape )
+
+ if ( *next == '\\' && ! escape ) {
escape = 1;
- else
+ } else {
escape = 0;
- if ( balance )
- next++;
+ }
+
+ if ( balance ) next++;
+ }
+
+ if ( balance != 0 ) {
+ rc = -1;
+ goto done;
}
- if ( balance != 0 )
- return( -1 );
*next = '\0';
+
if ( put_simple_filter( ber, str ) == -1 ) {
- return( -1 );
+ rc = -1;
+ goto done;
}
- *next++ = ')';
+
+ *next++ = /*'('*/ ')';
+
str = next;
parens--;
break;
break;
case /*'('*/ ')':
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: end\n", 0,0,0 );
+#else
Debug( LDAP_DEBUG_TRACE, "put_filter: end\n",
0, 0, 0 );
- if ( ber_printf( ber, /*"["*/ "]" ) == -1 )
- return( -1 );
+#endif
+ if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
str++;
parens--;
break;
break;
default: /* assume it's a simple type=value filter */
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, DETAIL1, "ldap_pvt_put_filter: default\n", 0,0,0 );
+#else
Debug( LDAP_DEBUG_TRACE, "put_filter: default\n",
0, 0, 0 );
+#endif
next = strchr( str, '\0' );
if ( put_simple_filter( ber, str ) == -1 ) {
- return( -1 );
+ rc = -1;
+ goto done;
}
str = next;
break;
}
}
- return( parens ? -1 : 0 );
+ rc = parens ? -1 : 0;
+
+done:
+ LDAP_FREE( freeme );
+ return rc;
}
/*
char *next = NULL;
char save;
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, ARGS, "put_filter_list \"%s\"\n", str,0,0 );
+#else
Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n",
str, 0, 0 );
+#endif
while ( *str ) {
while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
if ( *str == '\0' ) break;
if ( (next = find_right_paren( str + 1 )) == NULL ) {
- return( -1 );
+ return -1;
}
save = *++next;
/* now we have "(filter)" with str pointing to it */
*next = '\0';
- if ( ldap_int_put_filter( ber, str ) == -1 ) return -1;
+ if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1;
*next = save;
str = next;
ber_tag_t ftype;
int rc = -1;
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, ARGS, "put_simple_filter: \"%s\"\n", str,0,0 );
+#else
Debug( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n",
str, 0, 0 );
+#endif
str = LDAP_STRDUP( str );
if( str == NULL ) return -1;
case '<':
ftype = LDAP_FILTER_LE;
*s = '\0';
- if(! ldap_is_desc( str ) ) goto done;
break;
case '>':
ftype = LDAP_FILTER_GE;
*s = '\0';
- if(! ldap_is_desc( str ) ) goto done;
break;
case '~':
ftype = LDAP_FILTER_APPROX;
*s = '\0';
- if(! ldap_is_desc( str ) ) goto done;
break;
case ':':
char *dn = strchr( str, ':' );
char *rule = NULL;
- if( dn == NULL ) {
- if(! ldap_is_desc( str ) ) goto done;
-
- } else {
-
+ if( dn != NULL ) {
*dn++ = '\0';
rule = strchr( dn, ':' );
if( rc != -1 && rule && *rule != '\0' ) {
rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
}
+
if( rc != -1 && *str != '\0' ) {
rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
}
ber_slen_t len = ldap_pvt_filter_value_unescape( value );
if( len >= 0 ) {
- rc = ber_printf( ber, /*"{"*/ "totbN}",
- LDAP_FILTER_EXT_VALUE, value, len,
- LDAP_FILTER_EXT_DNATTRS, dn != NULL);
+ rc = ber_printf( ber, "to",
+ LDAP_FILTER_EXT_VALUE, value, len );
} else {
rc = -1;
}
}
+
+ if( rc != -1 && dn ) {
+ rc = ber_printf( ber, "tb",
+ LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 );
+ }
+
+ if( rc != -1 ) {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
}
goto done;
default:
- {
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+
+ } else {
char *nextstar = ldap_pvt_find_wildcard( value );
+
if ( nextstar == NULL ) {
goto done;
+
} else if ( *nextstar == '\0' ) {
ftype = LDAP_FILTER_EQUALITY;
+
} else if ( strcmp( value, "*" ) == 0 ) {
ftype = LDAP_FILTER_PRESENT;
+
} else {
rc = put_substring_filter( ber, str, value );
goto done;
} break;
}
+ if( !ldap_is_desc( str ) ) goto done;
+
if ( ftype == LDAP_FILTER_PRESENT ) {
rc = ber_printf( ber, "ts", ftype, str );
int gotstar = 0;
ber_tag_t ftype = LDAP_FILTER_SUBSTRINGS;
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, ARGS, "put_substring_filter \"%s=%s\"\n", type, val, 0 );
+#else
Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n",
type, val, 0 );
+#endif
- if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 )
- return( -1 );
+ if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) {
+ return -1;
+ }
for( ; *val; val=nextstar ) {
nextstar = ldap_pvt_find_wildcard( val );
if ( *nextstar == '\0' ) {
ftype = LDAP_SUBSTRING_FINAL;
- } else if ( gotstar++ == 0 ) {
- ftype = LDAP_SUBSTRING_INITIAL;
} else {
- ftype = LDAP_SUBSTRING_ANY;
+ *nextstar++ = '\0';
+ if ( gotstar++ == 0 ) {
+ ftype = LDAP_SUBSTRING_INITIAL;
+ } else {
+ ftype = LDAP_SUBSTRING_ANY;
+ }
}
- *nextstar++ = '\0';
-
- if ( *val != '\0' ) {
+ if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) {
ber_slen_t len = ldap_pvt_filter_value_unescape( val );
if ( len < 0 ) {
}
}
- if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 )
+ if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) {
return -1;
+ }
+
+ return 0;
+}
+
+static int
+put_vrFilter( BerElement *ber, const char *str_in )
+{
+ int rc;
+ char *freeme;
+ char *str;
+ char *next;
+ int parens, balance, escape;
+
+ /*
+ * A ValuesReturnFilter looks like this:
+ *
+ * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
+ * SimpleFilterItem ::= CHOICE {
+ * equalityMatch [3] AttributeValueAssertion,
+ * substrings [4] SubstringFilter,
+ * greaterOrEqual [5] AttributeValueAssertion,
+ * lessOrEqual [6] AttributeValueAssertion,
+ * present [7] AttributeType,
+ * approxMatch [8] AttributeValueAssertion,
+ * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3
+ * }
+ *
+ * SubstringFilter ::= SEQUENCE {
+ * type AttributeType,
+ * SEQUENCE OF CHOICE {
+ * initial [0] IA5String,
+ * any [1] IA5String,
+ * final [2] IA5String
+ * }
+ * }
+ *
+ * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3
+ * matchingRule [1] MatchingRuleId OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue }
+ */
+
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, ARGS, "put_vrFilter: \"%s\"\n", str_in, 0, 0 );
+#else
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 );
+#endif
+
+ freeme = LDAP_STRDUP( str_in );
+ if( freeme == NULL ) return LDAP_NO_MEMORY;
+ str = freeme;
+
+ parens = 0;
+ while ( *str ) {
+ switch ( *str ) {
+ case '(': /*')'*/
+ str++;
+ parens++;
+
+ /* skip spaces */
+ while( LDAP_SPACE( *str ) ) str++;
+
+ switch ( *str ) {
+ case '(':
+ if ( (next = find_right_paren( str )) == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next = '\0';
+
+ if ( put_vrFilter_list( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+
+ /* close the '(' */
+ *next++ = ')';
+
+ str = next;
+
+ parens--;
+ break;
+
+
+ default:
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, DETAIL1,
+ "put_vrFilter: simple\n", 0, 0, 0 );
+#else
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n",
+ 0, 0, 0 );
+#endif
+
+ balance = 1;
+ escape = 0;
+ next = str;
+
+ while ( *next && balance ) {
+ if ( escape == 0 ) {
+ if ( *next == '(' ) {
+ balance++;
+ } else if ( *next == ')' ) {
+ balance--;
+ }
+ }
+
+ if ( *next == '\\' && ! escape ) {
+ escape = 1;
+ } else {
+ escape = 0;
+ }
+
+ if ( balance ) next++;
+ }
+
+ if ( balance != 0 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next = '\0';
+
+ if ( put_simple_vrFilter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next++ = /*'('*/ ')';
+
+ str = next;
+ parens--;
+ break;
+ }
+ break;
+
+ case /*'('*/ ')':
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, DETAIL1, "put_vrFilter: end\n", 0, 0, 0 );
+#else
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter: end\n",
+ 0, 0, 0 );
+#endif
+ if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str++;
+ parens--;
+ break;
+
+ case ' ':
+ str++;
+ break;
+
+ default: /* assume it's a simple type=value filter */
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, DETAIL1, "put_vrFilter: default\n",
+ 0, 0, 0 );
+#else
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter: default\n",
+ 0, 0, 0 );
+#endif
+ next = strchr( str, '\0' );
+ if ( put_simple_vrFilter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str = next;
+ break;
+ }
+ }
+
+ rc = parens ? -1 : 0;
+
+done:
+ LDAP_FREE( freeme );
+ return rc;
+}
+
+int
+ldap_put_vrFilter( BerElement *ber, const char *str_in )
+{
+ int rc =0;
+
+ if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
+ rc = -1;
+ }
+
+ rc = put_vrFilter( ber, str_in );
+
+ if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int
+put_vrFilter_list( BerElement *ber, char *str )
+{
+ char *next = NULL;
+ char save;
+
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, ARGS, "put_vrFilter_list \"%s\"\n", str, 0, 0 );
+#else
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n",
+ str, 0, 0 );
+#endif
+
+ while ( *str ) {
+ while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
+ str++;
+ }
+ if ( *str == '\0' ) break;
+
+ if ( (next = find_right_paren( str + 1 )) == NULL ) {
+ return -1;
+ }
+ save = *++next;
+
+ /* now we have "(filter)" with str pointing to it */
+ *next = '\0';
+ if ( put_vrFilter( ber, str ) == -1 ) return -1;
+ *next = save;
+ str = next;
+ }
return 0;
}
+
+static int
+put_simple_vrFilter(
+ BerElement *ber,
+ char *str )
+{
+ char *s;
+ char *value;
+ ber_tag_t ftype;
+ int rc = -1;
+
+#ifdef NEW_LOGGING
+ LDAP_LOG ( FILTER, ARGS, "put_simple_vrFilter: \"%s\"\n", str, 0, 0 );
+#else
+ Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n",
+ str, 0, 0 );
+#endif
+
+ str = LDAP_STRDUP( str );
+ if( str == NULL ) return -1;
+
+ if ( (s = strchr( str, '=' )) == NULL ) {
+ goto done;
+ }
+
+ value = s + 1;
+ *s-- = '\0';
+
+ switch ( *s ) {
+ case '<':
+ ftype = LDAP_FILTER_LE;
+ *s = '\0';
+ break;
+
+ case '>':
+ ftype = LDAP_FILTER_GE;
+ *s = '\0';
+ break;
+
+ case '~':
+ ftype = LDAP_FILTER_APPROX;
+ *s = '\0';
+ break;
+
+ case ':':
+ /* According to ValuesReturnFilter control definition
+ * extensible filters are off the form:
+ * type [:rule] := value
+ * or :rule := value
+ */
+ ftype = LDAP_FILTER_EXT;
+ *s = '\0';
+
+ {
+ char *rule = strchr( str, ':' );
+ *rule++ = '\0';
+
+ if( rule == NULL ) {
+ /* must have attribute */
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+ }
+ rule = "";
+ }
+
+ if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
+ /* must have either type or rule */
+ goto done;
+ }
+
+ if ( *str != '\0' && !ldap_is_desc( str ) ) {
+ goto done;
+ }
+
+ if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
+ goto done;
+ }
+
+ rc = ber_printf( ber, "t{" /*"}"*/, ftype );
+
+ if( rc != -1 && rule && *rule != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
+ }
+
+ if( rc != -1 && *str != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
+ }
+
+ if( rc != -1 ) {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "to",
+ LDAP_FILTER_EXT_VALUE, value, len );
+ } else {
+ rc = -1;
+ }
+ }
+
+ if( rc != -1 ) {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
+ }
+ goto done;
+
+ default:
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+
+ } else {
+ char *nextstar = ldap_pvt_find_wildcard( value );
+
+ if ( nextstar == NULL ) {
+ goto done;
+
+ } else if ( *nextstar == '\0' ) {
+ ftype = LDAP_FILTER_EQUALITY;
+
+ } else if ( strcmp( value, "*" ) == 0 ) {
+ ftype = LDAP_FILTER_PRESENT;
+
+ } else {
+ rc = put_substring_filter( ber, str, value );
+ goto done;
+ }
+ } break;
+ }
+
+ if( !ldap_is_desc( str ) ) goto done;
+
+ if ( ftype == LDAP_FILTER_PRESENT ) {
+ rc = ber_printf( ber, "ts", ftype, str );
+
+ } else {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "t{soN}",
+ ftype, str, value, len );
+ }
+ }
+
+done:
+ if( rc != -1 ) rc = 0;
+ LDAP_FREE( str );
+ return rc;
+}
+