/** BEGIN COPYRIGHT BLOCK * This Program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; version 2 of the License. * * This Program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. * * In addition, as a special exception, Red Hat, Inc. gives You the additional * right to link the code of this Program with code not covered under the GNU * General Public License ("Non-GPL Code") and to distribute linked combinations * including the two, subject to the limitations in this paragraph. Non-GPL Code * permitted under this exception must only link to the code of this Program * through those well defined interfaces identified in the file named EXCEPTION * found in the source code files (the "Approved Interfaces"). The files of * Non-GPL Code may instantiate templates or use macros or inline functions from * the Approved Interfaces without causing the resulting work to be covered by * the GNU General Public License. Only Red Hat, Inc. may make changes or * additions to the list of Approved Interfaces. You must obey the GNU General * Public License in all respects for all of the Program code and other code used * in conjunction with the Program except the Non-GPL Code covered by this * exception. If you modify this file, you may extend this exception to your * version of the file, but you are not obligated to do so. If you do not wish to * provide this exception without modification, you must delete this exception * statement from your version and license this file solely under the GPL without * exception. * * * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. * Copyright (C) 2005 Red Hat, Inc. * All rights reserved. * END COPYRIGHT BLOCK **/ #ifdef HAVE_CONFIG_H # include #endif /* attr.c - routines for dealing with attributes */ #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #endif #include "slap.h" #undef DEBUG /* disable counters */ #include static int counters_created= 0; PR_DEFINE_COUNTER(slapi_attr_counter_created); PR_DEFINE_COUNTER(slapi_attr_counter_deleted); PR_DEFINE_COUNTER(slapi_attr_counter_exist); /* * structure used within AVL value trees. */ typedef struct slapi_attr_value_index { int savi_index; /* index into a_vals[] */ struct berval *savi_normval; /* normalized value */ } SlapiAttrValueIndex; /* * Utility function used by slapi_attr_type_cmp to * find the next component of an attribute type. */ static const char * next_comp( const char *s ) { while ( *s && *s != ';' ) { s++; } if ( *s == '\0' ) { return( NULL ); } else { return( s + 1 ); } } /* * Utility function used by slapi_attr_type_cmp to * compare two components of an attribute type. */ static int comp_cmp( const char *s1, const char *s2 ) { while ( *s1 && *s1 != ';' && tolower( *s1 ) == tolower( *s2 ) ) { s1++, s2++; } if ( *s1 != *s2 ) { if ( (*s1 == '\0' || *s1 == ';') && (*s2 == '\0' || *s2 == ';') ) { return( 0 ); } else { return( 1 ); } } else { return( 0 ); } } int slapi_attr_type_cmp( const char *a1, const char *a2, int opt ) { int rc= 0; switch ( opt ) { case SLAPI_TYPE_CMP_EXACT: /* compare base name + options as given */ rc = strcasecmp( a1, a2 ); break; case SLAPI_TYPE_CMP_BASE: /* ignore options on both names - compare base names only */ rc = comp_cmp( a1, a2 ); break; case SLAPI_TYPE_CMP_SUBTYPE: /* ignore options on second name that are not in first name */ /* * first, check that the base types match */ if ( comp_cmp( a1, a2 ) != 0 ) { rc = 1; break; } /* * next, for each component in a1, make sure there is a * matching component in a2. the order must be the same, * so we can keep looking where we left off each time in a2 */ rc = 0; for ( a1 = next_comp( a1 ); a1 != NULL; a1 = next_comp( a1 ) ) { for ( a2 = next_comp( a2 ); a2 != NULL; a2 = next_comp( a2 ) ) { if ( comp_cmp( a1, a2 ) == 0 ) { break; } } if ( a2 == NULL ) { rc = 1; break; } } break; } return( rc ); } /* * Return 1 if the two types are equivalent -- either the same type name, * or aliases for one another, including OIDs. * * Objective: don't allocate any memory! */ int slapi_attr_types_equivalent(const char *t1, const char *t2) { int retval = 0; struct asyntaxinfo *asi1, *asi2; if (NULL == t1 || NULL == t2) { return 0; } asi1 = attr_syntax_get_by_name(t1); asi2 = attr_syntax_get_by_name(t2); if (NULL != asi1) { if (NULL != asi2) { /* Both found - compare normalized names */ if (strcasecmp(asi1->asi_name, asi2->asi_name) == 0) { retval = 1; } else { retval = 0; } } else { /* One found, the other wasn't, so not equivalent */ retval = 0; } } else if (NULL != asi2) { /* One found, the other wasn't, so not equivalent */ retval = 0; } else { /* Neither found - perform case-insensitive compare */ if (strcasecmp(t1, t2) == 0) { retval = 1; } else { retval = 0; } } attr_syntax_return( asi1 ); attr_syntax_return( asi2 ); return retval; } Slapi_Attr * slapi_attr_new() { Slapi_Attr *a= (Slapi_Attr *)slapi_ch_calloc( 1, sizeof(Slapi_Attr)); if(!counters_created) { PR_CREATE_COUNTER(slapi_attr_counter_created,"Slapi_Attr","created",""); PR_CREATE_COUNTER(slapi_attr_counter_deleted,"Slapi_Attr","deleted",""); PR_CREATE_COUNTER(slapi_attr_counter_exist,"Slapi_Attr","exist",""); counters_created= 1; } PR_INCREMENT_COUNTER(slapi_attr_counter_created); PR_INCREMENT_COUNTER(slapi_attr_counter_exist); return a; } Slapi_Attr * slapi_attr_init(Slapi_Attr *a, const char *type) { return slapi_attr_init_locking_optional(a, type, PR_TRUE); } Slapi_Attr * slapi_attr_init_locking_optional(Slapi_Attr *a, const char *type, PRBool use_lock) { PR_ASSERT(a!=NULL); if(NULL != a) { struct asyntaxinfo *asi= NULL; const char *basetype= NULL; char *tmp= NULL; if(type!=NULL) { char buf[SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH]; basetype = buf; tmp = slapi_attr_basetype(type, buf, sizeof(buf)); if(tmp != NULL) { basetype = tmp; /* basetype was malloc'd */ } asi = attr_syntax_get_by_name_locking_optional(basetype, use_lock); } if(NULL == asi) { a->a_type = attr_syntax_normalize_no_lookup( type ); /* * no syntax for this type... return Octet String * syntax. we accomplish this by looking up a well known * attribute type that has that syntax. */ asi = attr_syntax_get_by_name_locking_optional( ATTR_WITH_OCTETSTRING_SYNTAX, use_lock); } else { char *attroptions = NULL; if ( NULL != type ) { attroptions = strchr( type, ';' ); } if ( NULL == attroptions ) { a->a_type = slapi_ch_strdup(asi->asi_name); } else { /* * If the original type includes any attribute options, * the a_type field is set to the type contained in the * attribute syntax followed by a normalized copy of the * options. */ char *normalized_options; normalized_options = attr_syntax_normalize_no_lookup( attroptions ); a->a_type = slapi_ch_smprintf("%s%s", asi->asi_name, normalized_options ); slapi_ch_free_string( &normalized_options ); } } if ( asi != NULL ) { a->a_plugin = asi->asi_plugin; a->a_flags = asi->asi_flags; a->a_mr_eq_plugin = asi->asi_mr_eq_plugin; a->a_mr_ord_plugin = asi->asi_mr_ord_plugin; a->a_mr_sub_plugin = asi->asi_mr_sub_plugin; } else { a->a_plugin = NULL; /* XXX - should be rare */ a->a_flags = 0; /* XXX - should be rare */ a->a_mr_eq_plugin = NULL; a->a_mr_ord_plugin = NULL; a->a_mr_sub_plugin = NULL; } attr_syntax_return_locking_optional( asi, use_lock ); if (NULL != tmp) { slapi_ch_free_string(&tmp); } slapi_valueset_init(&a->a_present_values); slapi_valueset_init(&a->a_deleted_values); a->a_listtofree= NULL; a->a_deletioncsn= NULL; a->a_next= NULL; } return a; } Slapi_Attr * slapi_attr_dup(const Slapi_Attr *attr) { Slapi_Attr *newattr= slapi_attr_new(); Slapi_Value **present_va= valueset_get_valuearray(&attr->a_present_values); /* JCM Mucking about inside the value set */ Slapi_Value **deleted_va= valueset_get_valuearray(&attr->a_deleted_values); /* JCM Mucking about inside the value set */ slapi_attr_init(newattr, attr->a_type); valueset_add_valuearray( &newattr->a_present_values, present_va ); valueset_add_valuearray( &newattr->a_deleted_values, deleted_va ); newattr->a_deletioncsn= csn_dup(attr->a_deletioncsn); return newattr; } void slapi_attr_free( Slapi_Attr **ppa ) { if(ppa!=NULL && *ppa!=NULL) { Slapi_Attr *a= *ppa; attr_done(a); slapi_ch_free( (void**)&a ); PR_INCREMENT_COUNTER(slapi_attr_counter_deleted); PR_DECREMENT_COUNTER(slapi_attr_counter_exist); } } void attr_done(Slapi_Attr *a) { if(a!=NULL) { slapi_ch_free((void**)&a->a_type); csn_free(&a->a_deletioncsn); slapi_valueset_done(&a->a_present_values); slapi_valueset_done(&a->a_deleted_values); { struct bervals2free *freelist; struct bervals2free *tmp; freelist = a->a_listtofree; while(freelist) { ber_bvecfree(freelist->bvals); tmp=freelist; freelist = freelist->next; slapi_ch_free((void **)&tmp); } } } } /* * slapi_attr_basetype - extracts the attribute base type (without * any attribute description options) from type. puts the result * in buf if it can, otherwise, it malloc's and returns the base * which should be free()'ed later. */ char * slapi_attr_basetype( const char *type, char *buf, size_t bufsize ) { unsigned int i; i = 0; while ( *type && *type != ';' && i < bufsize ) { buf[i++] = *type++; } if ( i < bufsize ) { buf[i] = '\0'; return( NULL ); } else { int len; char *tmp; len = strlen( type ); tmp = slapi_ch_malloc( len + 1 ); slapi_attr_basetype( type, tmp, len + 1 ); return( tmp ); } } /* * returns 0 if "v" is already a value within "a" and non-zero if not. */ int slapi_attr_value_find( const Slapi_Attr *a, const struct berval *v ) { struct ava ava; if ( NULL == a ) { return( -1 ); } ava.ava_type = a->a_type; ava.ava_value = *v; return(plugin_call_syntax_filter_ava( a, LDAP_FILTER_EQUALITY, &ava )); } int slapi_attr_get_type( Slapi_Attr *a, char **type ) { *type = a->a_type; return( 0 ); } /* * Fetch a copy of the values as an array of struct berval *'s. * Returns 0 upon success and non-zero on failure. * Free the array of values by calling ber_bvecfree(). */ int slapi_attr_get_bervals_copy( Slapi_Attr *a, struct berval ***vals ) { int retVal= 0; if ( NULL == vals ) { return -1; } if(NULL==a || valueset_isempty(&a->a_present_values)) { *vals = NULL; } else { Slapi_Value **va= valueset_get_valuearray(&a->a_present_values); valuearray_get_bervalarray(va,vals); } return retVal; } /* * JCM: BEWARE.. HIGHLY EVIL.. DO NOT USE THIS FUNCTION. * XXXmcs: Why not? Because it is only provided as a not-quite-backwards * compatible interface for older (pre-iDS 5.0) plugins. It works by * making a copy of the attribute values (the pre-iDS 5.0 function with * the same name returned a pointer into the Slapi_Attr structure -- no * copying). Since older users of this interface did not have to free * the values, this function arranges to free them WHEN THE Slapi_Attr * IS DESTROYED. This is accomplished by adding a pointer to the newly * allocated copy to a "to be freed" linked list inside the Slapi_Attr. * But if the Slapi_Attr is not destroyed very often, and this function * is called repeatedly, memory usage will grow without bound. Not good. * The value copies are freed inside attr_done() which is called from * slapi_attr_free() and a few other places. * * If you really want a copy of the values as a struct berval ** array, * call slapi_attr_get_bervals_copy() and free it yourself by calling * ber_bvecfree(). */ int slapi_attr_get_values( Slapi_Attr *a, struct berval ***vals ) { int retVal = slapi_attr_get_bervals_copy( a, vals ); if ( 0 == retVal ) { struct bervals2free *newfree; newfree = (struct bervals2free *)slapi_ch_malloc(sizeof(struct bervals2free)); newfree->next = a->a_listtofree; newfree->bvals = *vals; a->a_listtofree = newfree; } return retVal; } /* * Fetch a copy of the present valueset. * Caller must free the valueset. */ int slapi_attr_get_valueset(const Slapi_Attr *a, Slapi_ValueSet **vs) { int retVal= 0; if(vs!=NULL) { *vs= valueset_dup(&a->a_present_values); } return retVal; } /* * Careful... this returns a pointer to the contents! */ Slapi_Value ** attr_get_present_values(const Slapi_Attr *a) { return valueset_get_valuearray(&a->a_present_values); } int slapi_attr_get_flags( const Slapi_Attr *a, unsigned long *flags ) { *flags = a->a_flags; return( 0 ); } int slapi_attr_flag_is_set( const Slapi_Attr *a, unsigned long flag ) { return( a->a_flags & flag ); } int slapi_attr_value_cmp( const Slapi_Attr *a, const struct berval *v1, const struct berval *v2 ) { int retVal; if ( a->a_flags & SLAPI_ATTR_FLAG_CMP_BITBYBIT ) { int cmplen = ( v1->bv_len <= v2->bv_len ? v1->bv_len : v2->bv_len ); retVal = memcmp(v1->bv_val, v2->bv_val, cmplen); if ( retVal == 0 && v1->bv_len < v2->bv_len ) { retVal = -1; } else if ( retVal == 0 && v1->bv_len > v2->bv_len ) { retVal = 1; } } else { Slapi_Attr a2; struct ava ava; Slapi_Value *cvals[2]; Slapi_Value tmpcval; a2 = *a; cvals[0] = &tmpcval; cvals[0]->v_csnset = NULL; cvals[0]->bv = *v1; cvals[1] = NULL; a2.a_present_values.va = cvals; /* JCM - PUKE */ ava.ava_type = a->a_type; ava.ava_value = *v2; retVal = plugin_call_syntax_filter_ava(&a2, LDAP_FILTER_EQUALITY, &ava); } return retVal; } /* * Set the CSN of all the present values. */ int attr_set_csn( Slapi_Attr *a, const CSN *csn) { PR_ASSERT(a!=NULL); valueset_update_csn(&a->a_present_values, CSN_TYPE_VALUE_UPDATED, csn); return 0; } int attr_set_deletion_csn( Slapi_Attr *a, const CSN *csn) { PR_ASSERT(a!=NULL); if(csn_compare(csn,a->a_deletioncsn)>0) { csn_free(&a->a_deletioncsn); a->a_deletioncsn= csn_dup(csn); } return 0; } const CSN * attr_get_deletion_csn(const Slapi_Attr *a) { PR_ASSERT(a!=NULL); return a->a_deletioncsn; } int slapi_attr_first_value( Slapi_Attr *a, Slapi_Value **v ) { int rc; if(NULL == a) { rc = -1; } else { rc=slapi_valueset_first_value( &a->a_present_values, v ); } return rc; } int slapi_attr_next_value( Slapi_Attr *a, int hint, Slapi_Value **v) { int rc; if(NULL == a) { rc = -1; } else { rc=slapi_valueset_next_value( &a->a_present_values, hint, v ); } return rc; } int slapi_attr_get_numvalues( const Slapi_Attr *a, int *numValues ) { if(NULL == a) { *numValues = 0; } else { *numValues = slapi_valueset_count(&a->a_present_values); } return 0; } int attr_first_deleted_value( Slapi_Attr *a, Slapi_Value **v ) { return slapi_valueset_first_value( &a->a_deleted_values, v ); } int attr_next_deleted_value( Slapi_Attr *a, int hint, Slapi_Value **v) { return slapi_valueset_next_value( &a->a_deleted_values, hint, v ); } /* * Note: We are passing in the entry so that we may be able to "optimize" * the csn related information and roll it up higher to the level of entry. */ void attr_purge_state_information(Slapi_Entry *entry, Slapi_Attr *attr, const CSN *csnUpTo) { if(!valueset_isempty(&attr->a_deleted_values)) { valueset_purge(&attr->a_deleted_values, csnUpTo); } } /* * Search an attribute for a value, it could be present, deleted, or not present. */ int attr_value_find_wsi(Slapi_Attr *a, const struct berval *bval, Slapi_Value **value) { int retVal=0; struct ava ava; PR_ASSERT(a!=NULL); PR_ASSERT(value!=NULL); /* * we will first search the present values, and then, if * necessary, the deleted values. */ ava.ava_type = a->a_type; ava.ava_value = *bval; retVal = plugin_call_syntax_filter_ava_sv(a, LDAP_FILTER_EQUALITY, &ava, value, 0 /* Present */); if(retVal==0) { /* we found the value, so we don't search the deleted list */ retVal= VALUE_PRESENT; } else { retVal = plugin_call_syntax_filter_ava_sv(a, LDAP_FILTER_EQUALITY, &ava, value, 1 /* Deleted */); if(retVal==0) { /* It was on the deleted value list */ retVal= VALUE_DELETED; } else { /* Couldn't find it */ retVal= VALUE_NOTFOUND; } } return retVal; } int slapi_attr_add_value(Slapi_Attr *a, const Slapi_Value *v) { slapi_valueset_add_value( &a->a_present_values, v); return 0; } int slapi_attr_set_type(Slapi_Attr *a, const char *type) { int rc = 0; if((NULL == a) || (NULL == type)) { rc = -1; } else { slapi_ch_free_string(&a->a_type); a->a_type = slapi_ch_strdup(type); } return rc; } /* Make the valuset in Slapi_Attr be *vs--not a copy */ int slapi_attr_set_valueset(Slapi_Attr *a, const Slapi_ValueSet *vs) { slapi_valueset_set_valueset( &a->a_present_values, vs); return 0; } int attr_add_deleted_value(Slapi_Attr *a, const Slapi_Value *v) { slapi_valueset_add_value( &a->a_deleted_values, v); return 0; } /* * Add a value array to an attribute. * If more than one values are being added, we build an AVL tree of any existing * values and then update that in parallel with the existing values. This * AVL tree is used to detect the duplicates not only between the existing * values and to-be-added values but also among the to-be-added values. * The AVL tree is created and destroyed all within this function. * * Returns * LDAP_SUCCESS - OK * LDAP_OPERATIONS_ERROR - Existing duplicates in attribute. * LDAP_TYPE_OR_VALUE_EXISTS - Duplicate values. */ int attr_add_valuearray(Slapi_Attr *a, Slapi_Value **vals, const char *dn) { int i = 0; int numofvals = 0; int duplicate_index = -1; int was_present_null = 0; int rc = LDAP_SUCCESS; if (valuearray_isempty(vals)) { /* * No values to add (unexpected but acceptable). */ return rc; } /* * determine whether we should use an AVL tree of values or not */ for ( i = 0; vals[i] != NULL; i++ ) ; numofvals = i; /* * detect duplicate values */ if ( numofvals > 1 ) { /* * Several values to add: use an AVL tree to detect duplicates. */ LDAPDebug( LDAP_DEBUG_TRACE, "slapi_entry_add_values: using an AVL tree to " "detect duplicate values\n", 0, 0, 0 ); if (valueset_isempty(&a->a_present_values)) { /* if the attribute contains no values yet, just check the * input vals array for duplicates */ Avlnode *vtree = NULL; rc= valuetree_add_valuearray(a, vals, &vtree, &duplicate_index); valuetree_free(&vtree); was_present_null = 1; } else { /* the attr and vals both contain values, check intersection */ rc= valueset_intersectswith_valuearray(&a->a_present_values, a, vals, &duplicate_index); } } else if ( !valueset_isempty(&a->a_present_values) ) { /* * One or no value to add: don't bother constructing * an AVL tree, etc. since it probably isn't worth the time. */ for ( i = 0; vals[i] != NULL; ++i ) { if ( slapi_attr_value_find( a, slapi_value_get_berval(vals[i]) ) == 0 ) { duplicate_index = i; rc = LDAP_TYPE_OR_VALUE_EXISTS; break; } } } /* * add values if no duplicates detected */ if(rc==LDAP_SUCCESS) { valueset_add_valuearray( &a->a_present_values, vals ); } /* In the case of duplicate value, rc == LDAP_TYPE_OR_VALUE_EXISTS or * LDAP_OPERATIONS_ERROR */ else if ( duplicate_index >= 0 ) { char avdbuf[BUFSIZ]; char bvvalcopy[BUFSIZ]; char *duplicate_string = "null or non-ASCII"; i = 0; while ( (unsigned int)i < vals[duplicate_index]->bv.bv_len && i < BUFSIZ - 1 && vals[duplicate_index]->bv.bv_val[i] && isascii ( vals[duplicate_index]->bv.bv_val[i] )) { i++; } if ( i ) { if ( vals[duplicate_index]->bv.bv_val[i] == 0 ) { duplicate_string = vals[duplicate_index]->bv.bv_val; } else { strncpy ( &bvvalcopy[0], vals[duplicate_index]->bv.bv_val, i ); bvvalcopy[i] = '\0'; duplicate_string = bvvalcopy; } } slapi_log_error( SLAPI_LOG_TRACE, NULL, "add value \"%s\" to " "attribute type \"%s\" in entry \"%s\" failed: %s\n", duplicate_string, a->a_type, dn ? escape_string(dn,avdbuf) : "", (was_present_null ? "duplicate new value" : "value exists")); } return( rc ); } /* quickly toss an attribute's values and replace them with new ones * (used by attrlist_replace_fast) * Returns * LDAP_SUCCESS - OK * LDAP_OPERATIONS_ERROR - Existing duplicates in attribute. */ int attr_replace(Slapi_Attr *a, Slapi_Value **vals) { return valueset_replace(a, &a->a_present_values, vals); } int attr_check_onoff ( const char *attr_name, char *value, long minval, long maxval, char *errorbuf ) { int retVal = LDAP_SUCCESS; if ( strcasecmp ( value, "on" ) != 0 && strcasecmp ( value, "off") != 0 && strcasecmp ( value, "1" ) != 0 && strcasecmp ( value, "0" ) != 0 && strcasecmp ( value, "true" ) != 0 && strcasecmp ( value, "false" ) != 0 ) { PR_snprintf ( errorbuf, BUFSIZ, "%s: invalid value \"%s\".", attr_name, value ); retVal = LDAP_CONSTRAINT_VIOLATION; } return retVal; } int attr_check_minmax ( const char *attr_name, char *value, long minval, long maxval, char *errorbuf ) { int retVal = LDAP_SUCCESS; long val; val = strtol(value, NULL, 0); if ( (minval != -1 ? (val < minval ? 1 : 0) : 0) || (maxval != -1 ? (val > maxval ? 1 : 0) : 0) ) { PR_snprintf ( errorbuf, BUFSIZ, "%s: invalid value \"%s\".", attr_name, value ); retVal = LDAP_CONSTRAINT_VIOLATION; } return retVal; } /** Returns the function which can be used to compare (like memcmp/strcmp) two values of this type of attribute. The comparison function will use the ORDERING matching rule if available, or the default comparison function from the syntax plugin. Note: if there is no ORDERING matching rule, and the syntax does not provide an ordered compare function, this function will return LDAP_PROTOCOL_ERROR and compare_fn will be NULL. Returns LDAP_SUCCESS if successful and sets *compare_fn to the function. */ int attr_get_value_cmp_fn(const Slapi_Attr *attr, value_compare_fn_type *compare_fn) { int rc = LDAP_PROTOCOL_ERROR; LDAPDebug0Args(LDAP_DEBUG_TRACE, "=> slapi_attr_get_value_cmp_fn\n"); *compare_fn = NULL; if (attr == NULL) { LDAPDebug0Args(LDAP_DEBUG_TRACE, "<= slapi_attr_get_value_cmp_fn no attribute given\n"); rc = LDAP_PARAM_ERROR; /* unkonwn */ goto done; } if (attr->a_mr_ord_plugin && attr->a_mr_ord_plugin->plg_mr_compare) { *compare_fn = (value_compare_fn_type) attr->a_mr_ord_plugin->plg_mr_compare; rc = LDAP_SUCCESS; goto done; } if ((attr->a_plugin->plg_syntax_flags & SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING) == 0) { LDAPDebug2Args(LDAP_DEBUG_TRACE, "<= slapi_attr_get_value_cmp_fn syntax [%s] for attribute [%s] does not support ordering\n", attr->a_plugin->plg_syntax_oid, attr->a_type); goto done; } if (attr->a_plugin->plg_syntax_filter_ava == NULL) { LDAPDebug2Args(LDAP_DEBUG_TRACE, "<= slapi_attr_get_value_cmp_fn syntax [%s] for attribute [%s] does not support equality matching\n", attr->a_plugin->plg_syntax_oid, attr->a_type); goto done; } if (attr->a_plugin->plg_syntax_compare == NULL) { LDAPDebug2Args(LDAP_DEBUG_TRACE, "<= slapi_attr_get_value_cmp_fn syntax [%s] for attribute [%s] does not have a compare function\n", attr->a_plugin->plg_syntax_oid, attr->a_type); goto done; } *compare_fn = (value_compare_fn_type)attr->a_plugin->plg_syntax_compare; rc = LDAP_SUCCESS; done: LDAPDebug0Args(LDAP_DEBUG_TRACE, "<= slapi_attr_get_value_cmp_fn \n"); return rc; } const char * attr_get_syntax_oid(const Slapi_Attr *attr) { return attr->a_plugin->plg_syntax_oid; }