summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/vattr.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/slapd/vattr.c')
-rw-r--r--ldap/servers/slapd/vattr.c2387
1 files changed, 2387 insertions, 0 deletions
diff --git a/ldap/servers/slapd/vattr.c b/ldap/servers/slapd/vattr.c
new file mode 100644
index 00000000..ca360b08
--- /dev/null
+++ b/ldap/servers/slapd/vattr.c
@@ -0,0 +1,2387 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ Virtual Attributes
+
+ This file implements the "virtual attribute API", which is the interface
+ by which any joe servercode gets the values of attributes associated with an
+ entry which are _not_ stored with the entry. Attributes which _are_ stored
+ with the entry will also be returned by this interface, unless the caller
+ specifies the SLAPI_REALATTRS_ONLY flag. This means that the casual caller
+ looking for attribute values should probably call the virtual attribute interface
+ rather than the regular Slapi_Entry_Attr_Find... and so on calls.
+ This interface is different from that one in that the data returned is
+ a copy, to be freed by the caller. Details on how to free the returned data is
+ given individually for each function.
+
+ The thing is implemented with a service provider model. The code in here
+ does NOT know how to generate computed attr values etc, it just calls the
+ registered providers of such information. It also takes care of any crusty
+ special cases. Everything above the line here is crispy clean.
+
+ Implicit in this interface is the assumption that the value of an attribute
+ can only depend on the entry and some independent stored state. For example,
+ the value can't depend upon who is asking, since we don't pass that information
+ over the interface.
+
+ More design: we'd like to have the regular result returning code in result.c call this
+ interface. However, as it stands, his would incur a malloc, a copy and a free for every
+ value. Too expensive. So, it would be good to modify the interface such that when we
+ retrieve values from inside the entry, we just fetch a pointer like before. When we
+ retrieve values which are generated, we get copies of the data (or can we get pointers
+ there too ?? --- no).
+
+ One way to achieve this: allow the caller to say that they perfer to receive pointers
+ and not copies. They are then informed by the function whether they did receive pointers
+ or copies. They then call the free function only when needed. The implicit lifetime of
+ the returned pointers is the lifetime of the entry object passed into the function.
+ Nasty, but one has to do these things in the name of the god performance.
+
+ DBDB: remember to rename the structures mark complained weren't compliant with the slapi naming scheme.
+
+*/
+
+#include "slap.h"
+
+#include "vattr_spi.h"
+#include "statechange.h"
+
+#ifdef SOURCEFILE
+#undef SOURCEFILE
+#endif
+#define SOURCEFILE "vattr.c"
+static char *sourcefile = SOURCEFILE;
+
+/* Define only for module test code */
+/* #define VATTR_TEST_CODE */
+
+/* Loop context structure */
+struct _vattr_context {
+ Slapi_PBlock *pb;
+ unsigned int vattr_context_loop_count;
+ unsigned int error_displayed;
+};
+#define VATTR_LOOP_COUNT_MAX 50
+
+typedef vattr_sp_handle vattr_sp_handle_list;
+
+/* Local prototypes */
+static int vattr_map_create();
+static void vattr_map_destroy();
+int vattr_map_sp_insert(char *type_to_add, vattr_sp_handle *sp, void *hint);
+vattr_sp_handle_list *vattr_map_sp_getlist(char *type_to_find);
+vattr_sp_handle_list *vattr_map_namespace_sp_getlist(Slapi_DN *dn, const char *type_to_find);
+vattr_sp_handle_list *vattr_map_sp_get_complete_list();
+vattr_sp_handle *vattr_map_sp_first(vattr_sp_handle_list *list,void **hint);
+vattr_sp_handle *vattr_map_sp_next(vattr_sp_handle_list *list,void **hint);
+vattr_sp_handle *vattr_list_sp_first(vattr_sp_handle_list *list);
+vattr_sp_handle *vattr_list_sp_next(vattr_sp_handle_list *list);
+int vattr_call_sp_get_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *buffer_flags, void *hint);
+int vattr_call_sp_get_batch_values(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, char **type, Slapi_ValueSet*** results,int **type_name_disposition, char*** actual_type_name, int flags, int *buffer_flags, void** hint);
+int vattr_call_sp_compare_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, const char *type, Slapi_Value* test_this,int *result, int flags, void* hint);
+int vattr_call_sp_get_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags);
+void schema_changed_callback(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data);
+int slapi_vattrspi_register_internal(vattr_sp_handle **h, vattr_get_fn_type get_fn, vattr_get_ex_fn_type get_ex_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn, void *options);
+
+#ifdef VATTR_TEST_CODE
+int vattr_basic_sp_init();
+#endif
+
+void **statechange_api;
+
+/* Housekeeping Functions, called by server startup/shutdown code */
+
+/* Called on server startup, init all structures etc */
+void vattr_init()
+{
+ statechange_api = 0;
+ vattr_map_create();
+
+#ifdef VATTR_TEST_CODE
+ vattr_basic_sp_init();
+#endif
+}
+
+/* Called on server shutdown, free all structures, inform service providers that we're going down etc */
+void vattr_cleanup()
+{
+ vattr_map_destroy();
+}
+
+/* The public interface functions start here */
+
+/* Function which returns the value(s) of an attribute, given an entry and the attribute type name */
+/* Return 0 if OK, ?? if attr doesn't exist, ?? if some error condition ?? if result doesn't need to be free'ed */
+
+int slapi_vattr_values_get(/* Entry we're interested in */ Slapi_Entry *e, /* attr type name */ char *type, /* pointer to result set */ Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *buffer_flags)
+{
+ vattr_context *c = NULL;
+ return slapi_vattr_values_get_sp(c,e,type,results,type_name_disposition,actual_type_name,flags, buffer_flags);
+}
+
+int slapi_vattr_values_get_ex(/* Entry we're interested in */ Slapi_Entry *e,/* attr type name */ char *type, /* pointer to result set */ Slapi_ValueSet*** results,int **type_name_disposition, char*** actual_type_name, int flags, int *buffer_flags, int *subtype_count)
+{
+ vattr_context *c = NULL;
+ return slapi_vattr_values_get_sp_ex(c,e,type,results,type_name_disposition,actual_type_name,flags, buffer_flags, subtype_count);
+}
+
+int slapi_vattr_namespace_values_get(/* Entry we're interested in */ Slapi_Entry *e, /* backend namespace dn*/ Slapi_DN *namespace_dn, /* attr type name */ char *type, /* pointer to result set */ Slapi_ValueSet*** results,int **type_name_disposition, char*** actual_type_name, int flags, int *buffer_flags, int *subtype_count)
+{
+ vattr_context *c = NULL;
+ return slapi_vattr_namespace_values_get_sp(c,e,namespace_dn,type,results,type_name_disposition,actual_type_name,flags, buffer_flags, subtype_count);
+}
+
+
+/*
+ * If pointers into the entry were requested then we might have
+ * a stashed pointer to the entry values, otherwise will be null.
+ */
+Slapi_ValueSet *vattr_typethang_get_values(vattr_type_thang *t)
+{
+ return t->type_values;
+}
+
+/*
+ * This can be much faster than using slapi_vattr_values_get()
+ * when you have a vattr_type_thang list returned from slapi_vattr_list_types().
+ *
+ * We call the vattr SPs to get values for an attribute type in the list only
+ * if the results field for that attribute type is null.
+ * If the type list comes from slapi_vattr_list_types() then the value is null
+ * only for attributes for which an SP wishes to provide a value, so fo the other
+ * ones don't bother calling the SPs again--just use the real value we picked up
+ * from the entry.
+ *
+ */
+int slapi_vattr_values_type_thang_get(
+ Slapi_Entry *e,
+ vattr_type_thang *type_thang,
+ Slapi_ValueSet **results,
+ int *type_name_disposition,
+ char **actual_type_name,
+ int flags,
+ int *buffer_flags
+)
+{
+ int rc = 0;
+ char *type = NULL;
+
+ type = vattr_typethang_get_name(type_thang);
+ *results = vattr_typethang_get_values(type_thang);
+
+ if (*results != NULL) {
+ /* we already have a pointer directly into the entry */
+ *actual_type_name = type;
+ *type_name_disposition =
+ SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_POINTERS;
+ } else {
+ /* fall back to usual implementation */
+ rc = slapi_vattr_values_get(e,type,results,
+ type_name_disposition,
+ actual_type_name,
+ flags,buffer_flags);
+ }
+
+ return rc;
+}
+
+static void vattr_helper_get_entry_conts(Slapi_Entry *e,char *type, vattr_get_thang *my_get)
+{
+ Slapi_Attr *a = NULL;
+ void *dummy = 0;
+
+ a = attrlist_find_ex(e->e_attrs,type,&(my_get->get_name_disposition), &(my_get->get_type_name), &dummy);
+ if (a) {
+ my_get->get_present = 1;
+ my_get->get_present_values = &(a->a_present_values);
+ my_get->get_attr = a;
+ }
+}
+
+static int vattr_helper_get_entry_conts_with_subtypes(Slapi_Entry *e,const char *type, vattr_get_thang **my_get)
+{
+ Slapi_Attr *a = NULL;
+ void *hint = 0;
+ int counter = 0;
+ int attr_count = attrlist_count_subtypes(e->e_attrs,type);
+
+ if(attr_count > 0)
+ {
+ *my_get = (vattr_get_thang *)slapi_ch_calloc(attr_count, sizeof(vattr_get_thang));
+
+ /* pick up attributes with sub-types and slip into the get_thang list */
+ for(counter = 0; counter < attr_count; counter++)
+ {
+ a = attrlist_find_ex(e->e_attrs,type,&((*my_get)[counter].get_name_disposition), &((*my_get)[counter].get_type_name), &hint);
+ if (a) {
+ (*my_get)[counter].get_present = 1;
+ (*my_get)[counter].get_present_values = &(a->a_present_values);
+ (*my_get)[counter].get_attr = a;
+ }
+ }
+ }
+
+ return attr_count;
+}
+
+static int vattr_helper_get_entry_conts_no_subtypes(Slapi_Entry *e,const char *type, vattr_get_thang **my_get)
+{
+ int attr_count = 0;
+ Slapi_Attr *a = attrlist_find(e->e_attrs,type);
+
+ if (a) {
+ attr_count = 1;
+ *my_get = (vattr_get_thang *)slapi_ch_calloc(1, sizeof(vattr_get_thang));
+ (*my_get)[0].get_present = 1;
+ (*my_get)[0].get_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ (*my_get)[0].get_type_name = a->a_type;
+ (*my_get)[0].get_present_values = &(a->a_present_values);
+ (*my_get)[0].get_attr = a;
+ }
+
+ return attr_count;
+}
+
+static int vattr_helper_get_entry_conts_ex(Slapi_Entry *e,const char *type, vattr_get_thang **my_get, int suppress_subtypes)
+{
+ int rc;
+
+ if ( suppress_subtypes ) {
+ rc = vattr_helper_get_entry_conts_no_subtypes(e, type, my_get);
+ } else {
+ rc = vattr_helper_get_entry_conts_with_subtypes(e, type, my_get);
+ }
+
+ return rc;
+}
+
+
+vattr_context *vattr_context_new( Slapi_PBlock *pb )
+{
+ vattr_context *c = (vattr_context *)slapi_ch_calloc(1, sizeof(vattr_context));
+ /* The payload is zero, which is what we want */
+ if ( c ) {
+ c->pb = pb;
+ }
+
+ return c;
+}
+
+static int vattr_context_check(vattr_context *c)
+{
+ /* Observe the loop count and see if it's higher than is allowed */
+ if (c->vattr_context_loop_count > VATTR_LOOP_COUNT_MAX) {
+ return SLAPI_VIRTUALATTRS_LOOP_DETECTED;
+ } else return 0;
+}
+
+static void vattr_context_mark(vattr_context *c)
+{
+ c->vattr_context_loop_count += 1;
+}
+
+static int vattr_context_unmark(vattr_context *c)
+{
+ return (c->vattr_context_loop_count -= 1);
+}
+
+/* modify the context structure on exit from a vattr sp function */
+static void vattr_context_ungrok(vattr_context **c)
+{
+ /* Decrement the loop count */
+ if (0 == vattr_context_unmark(*c)) {
+ /* If necessary, delete the structure */
+ slapi_ch_free((void **)c);
+ }
+}
+
+/* Check and mess with the context structure on entry to a vattr sp function */
+static int vattr_context_grok(vattr_context **c)
+{
+ int rc = 0;
+ /* First check that we've not got into an infinite loop.
+ We do so by means of the vattr_context structure.
+ */
+
+ /* Do we have a context at all ?? */
+ if (NULL == *c) {
+ /* No, so let's make one */
+ *c = vattr_context_new( NULL );
+ if (NULL == *c) {
+ return ENOMEM;
+ }
+ } else {
+ /* Yes, so let's check its contents */
+ rc = vattr_context_check(*c);
+ }
+ /* mark the context as having been used once */
+ vattr_context_mark(*c);
+ return rc;
+}
+
+
+
+/* keep track of error messages so we don't spam the error log */
+static void vattr_context_set_loop_msg_displayed(vattr_context **c)
+{
+ (*c)->error_displayed = 1;
+}
+
+static int vattr_context_is_loop_msg_displayed(vattr_context **c)
+{
+ return (*c)->error_displayed;
+}
+
+/*
+ * vattr_test_filter:
+ *
+ * . tests an ava, presence or substring filter against e.
+ * . group these filter types together to avoid having to duplicate the
+ * . vattr specific code in three seperate routines.
+ * . handles virtual attrs in the filter.
+ * . does update the vattrcache if a call to this calculates vattrs
+ *
+ * returns: 0 filter matched
+ * -1 filter did not match
+ * >0 an ldap error code
+ *
+*/
+int vattr_test_filter( /* Entry we're interested in */ Slapi_Entry *e,
+ Slapi_Filter *f,
+ filter_type_t filter_type,
+ char * type) {
+ int rc = -1;
+ int sp_bit = 0; /* Set if an SP supplied an answer */
+ vattr_sp_handle_list *list = NULL;
+ Slapi_DN *sdn;
+ Slapi_Backend *be;
+ Slapi_DN *namespace_dn;
+
+ /* get the namespace this entry belongs to */
+ sdn = slapi_entry_get_sdn( e );
+ be = slapi_be_select( sdn );
+ namespace_dn = (Slapi_DN*)slapi_be_getsuffix(be, 0);
+
+ /* Look for attribute in the map */
+
+ if(namespace_dn)
+ {
+ list = vattr_map_namespace_sp_getlist(namespace_dn, type);
+ }
+ else
+ {
+ list = vattr_map_namespace_sp_getlist(NULL, type);
+ }
+
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+ Slapi_Attr *cache_attr = NULL;
+ /* first lets consult the cache to save work */
+ int cache_status;
+
+ cache_status = slapi_entry_vattrcache_findAndTest(e, type,
+ f,
+ filter_type,
+ &rc);
+ switch(cache_status)
+ {
+ case SLAPI_ENTRY_VATTR_RESOLVED_EXISTS: /* cached vattr */
+ {
+ sp_bit = 1;
+ break;
+ }
+
+ case SLAPI_ENTRY_VATTR_RESOLVED_ABSENT: /* does not exist */
+ break; /* look in entry */
+
+ case SLAPI_ENTRY_VATTR_NOT_RESOLVED: /* not resolved */
+ default: /* any other result, resolve */
+ {
+ int flags = SLAPI_VIRTUALATTRS_REQUEST_POINTERS;
+ void *hint = NULL;
+ Slapi_ValueSet **results = NULL; /* pointer to result set */
+ int *type_name_disposition;
+ char **actual_type_name;
+ int buffer_flags;
+ vattr_get_thang my_get = {0};
+ vattr_context ctx;
+ /* bit cacky, but need to make a null terminated lists for now
+ * for the (unimplemented and so fake) batch attribute request
+ */
+ char *types[2];
+ void *hint_list[2];
+
+ types[0] = type;
+ types[1] = 0;
+ hint_list[1] = 0;
+
+ /* set up some local context */
+ ctx.vattr_context_loop_count=1;
+ ctx.error_displayed = 0;
+
+ for (current_handle = vattr_map_sp_first(list,&hint); current_handle; current_handle = vattr_map_sp_next(current_handle,&hint))
+ {
+ hint_list[0] = hint;
+
+ rc = vattr_call_sp_get_batch_values(current_handle,&ctx,e,
+ &my_get,types,&results,&type_name_disposition,
+ &actual_type_name,flags,&buffer_flags, hint_list);
+
+ if (0 == rc)
+ {
+ sp_bit = 1;
+ break;
+ }
+ }
+
+ if(!sp_bit)
+ {
+ /*
+ * No vattr sp supplied an answer so will look in the
+ * entry itself.
+ * but first lets cache the no result
+ */
+ slapi_entry_vattrcache_merge_sv(e, type, NULL );
+
+ }
+ else
+ {
+ /*
+ * A vattr sp supplied an answer.
+ * so turn the value into a Slapi_Attr, pass
+ * to the syntax plugin for comparison.
+ */
+
+ if ( filter_type == FILTER_TYPE_AVA ||
+ filter_type == FILTER_TYPE_SUBSTRING ) {
+ Slapi_Attr *a = NULL;
+ int i=0;
+ /* may need this when we have a passin interface for set_valueset */
+ /*Slapi_ValueSet null_valueset = {0};*/
+
+ rc=-1;
+
+ if(results && actual_type_name && type_name_disposition)
+ {
+ while(results[i] && rc)
+ {
+ a = slapi_attr_new();
+ slapi_attr_init(a, type);
+ /* a now contains a *copy* of results */
+ slapi_attr_set_valueset( a, results[i]);
+
+ if ( filter_type == FILTER_TYPE_AVA ) {
+ rc = plugin_call_syntax_filter_ava( a,
+ f->f_choice, &f->f_ava );
+ } else if ( filter_type == FILTER_TYPE_SUBSTRING) {
+ rc = plugin_call_syntax_filter_sub( a,
+ &f->f_sub);
+ }
+
+ /*
+ * Cache stuff: dups results
+ */
+ slapi_entry_vattrcache_merge_sv(e, actual_type_name[i],
+ results[i] );
+ /*
+ * Free stuff, just in case we did not
+ * get pointers.
+ */
+ slapi_vattr_values_free( &(results[i]),
+ &(actual_type_name[i]),
+ buffer_flags);
+ /* may need this when we support a passin set_valueset */
+ /*slapi_attr_set_valueset( a, &null_valueset);*/
+ /* since a contains a copy of results, we must free it */
+ slapi_attr_free(&a);
+ i++;
+ }
+ }
+
+ slapi_ch_free((void**)&results);
+ slapi_ch_free((void**)&actual_type_name);
+ slapi_ch_free((void**)&type_name_disposition);
+
+ } else if ( filter_type == FILTER_TYPE_PRES ) {
+ /*
+ * Cache stuff: dups results
+ */
+ int i=0;
+
+ while(results[i])
+ {
+ slapi_entry_vattrcache_merge_sv(e, actual_type_name[i],
+ results[i] );
+ /*
+ * Free stuff, just in case we did not
+ * get pointers.
+ */
+ slapi_vattr_values_free( &results[i],
+ &actual_type_name[i],
+ buffer_flags);
+ }
+ slapi_ch_free((void**)&results);
+ slapi_ch_free((void**)&actual_type_name);
+ slapi_ch_free((void**)&type_name_disposition);
+ }
+ }
+
+ break;
+ }
+ }/* switch */
+ }
+ /* If no SP supplied the answer, take it from the entry */
+ if (!sp_bit)
+ {
+ int acl_test_done;
+
+ if ( filter_type == FILTER_TYPE_AVA ) {
+
+ rc = test_ava_filter( NULL /* pb not needed */,
+ e, e->e_attrs, &f->f_ava,
+ f->f_choice,
+ 0 /* no access check */,
+ 0 /* do test filter */,
+ &acl_test_done);
+
+ } else if ( filter_type == FILTER_TYPE_SUBSTRING ) {
+
+ rc = test_substring_filter( NULL, e, f, 0 /* no access check */,
+ 0 /* do test filter */, &acl_test_done);
+
+ } else if ( filter_type == FILTER_TYPE_PRES ) {
+
+ rc = test_presence_filter( NULL, e, f->f_type,
+ 0 /* no access check */,
+ 0 /* do test filter */,
+ &acl_test_done);
+ }
+ }
+ return rc;
+}
+/*
+ * deprecated in favour of slapi_vattr_values_get_sp_ex() which
+ * returns subtypes too.
+*/
+SLAPI_DEPRECATED int
+slapi_vattr_values_get_sp(vattr_context *c,
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* attr type name */ char *type,
+ /* pointer to result set */ Slapi_ValueSet** results,
+ int *type_name_disposition,
+ char** actual_type_name, int flags,
+ int *buffer_flags)
+{
+
+ PRBool use_local_ctx=PR_FALSE;
+ vattr_context ctx;
+ int rc = 0;
+ int sp_bit = 0; /* Set if an SP supplied an answer */
+ vattr_sp_handle_list *list = NULL;
+
+ vattr_get_thang my_get = {0};
+
+ if (c != NULL) {
+ rc = vattr_context_grok(&c);
+ if (0 != rc) {
+ if(!vattr_context_is_loop_msg_displayed(&c))
+ {
+ /* Print a handy error log message */
+ LDAPDebug(LDAP_DEBUG_ANY,"Detected virtual attribute loop in get on entry %s, attribute %s\n", slapi_entry_get_dn_const(e), type, 0);
+ vattr_context_set_loop_msg_displayed(&c);
+ }
+ return rc;
+ }
+ } else {
+ use_local_ctx=PR_TRUE;
+ ctx.vattr_context_loop_count=1;
+ ctx.error_displayed = 0;
+ }
+
+ /* For attributes which are in the entry, we just need to get to the Slapi_Attr structure and yank out the slapi_value_set
+ structure. We either return a pointer directly to it, or we copy it, depending upon whether the caller asked us to try to
+ avoid copying.
+ */
+
+ /* First grok the entry, and remember what we saw. This call does no more than walk down the entry attribute list, do some string compares and copy pointers. */
+ vattr_helper_get_entry_conts(e,type, &my_get);
+ /* Having done that, we now consult the attribute map to find service providers who are interested */
+ /* Look for attribute in the map */
+ if(!(flags & SLAPI_REALATTRS_ONLY))
+ {
+ list = vattr_map_sp_getlist(type);
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+ void *hint = NULL;
+ Slapi_Attr* cache_attr = 0;
+ char *vattr_type = NULL;
+ /* first lets consult the cache to save work */
+ int cache_status;
+
+ cache_status =
+ slapi_entry_vattrcache_find_values_and_type(e, type,
+ results,
+ actual_type_name);
+ switch(cache_status)
+ {
+ case SLAPI_ENTRY_VATTR_RESOLVED_EXISTS: /* cached vattr */
+ {
+ sp_bit = 1;
+
+ /* Complete analysis of type matching */
+ if ( 0 == slapi_attr_type_cmp( type , *actual_type_name, SLAPI_TYPE_CMP_EXACT) )
+ {
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ } else {
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE;
+ }
+
+ break;
+ }
+
+ case SLAPI_ENTRY_VATTR_RESOLVED_ABSENT: /* does not exist */
+ break; /* look in entry */
+
+ case SLAPI_ENTRY_VATTR_NOT_RESOLVED: /* not resolved */
+ default: /* any other result, resolve */
+ {
+ for (current_handle = vattr_map_sp_first(list,&hint); current_handle; current_handle = vattr_map_sp_next(current_handle,&hint))
+ {
+ if (use_local_ctx)
+ {
+ rc = vattr_call_sp_get_value(current_handle,&ctx,e,&my_get,type,results,type_name_disposition,actual_type_name,flags,buffer_flags, hint);
+ }
+ else
+ {
+ /* call this SP */
+ rc = vattr_call_sp_get_value(current_handle,c,e,&my_get,type,results,type_name_disposition,actual_type_name,flags,buffer_flags, hint);
+ }
+
+ if (0 == rc)
+ {
+ sp_bit = 1;
+ break;
+ }
+ }
+
+ if(!sp_bit)
+ {
+ /* clean up, we have failed and must now examine the
+ * entry itself
+ * But first lets cache the no result
+ * Creates the type (if necessary).
+ */
+ slapi_entry_vattrcache_merge_sv(e, type, NULL );
+
+ }
+ else
+ {
+ /*
+ * we need to cache the virtual attribute
+ * creates the type (if necessary) and dups
+ * results.
+ */
+ slapi_entry_vattrcache_merge_sv(e, *actual_type_name,
+ *results );
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ /* If no SP supplied the answer, take it from the entry */
+ if (!sp_bit && !(flags & SLAPI_VIRTUALATTRS_ONLY))
+ {
+ rc = 0; /* reset return code (cause an sp must have failed) */
+ *type_name_disposition = my_get.get_name_disposition;
+
+ if (my_get.get_present) {
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ *results = my_get.get_present_values;
+ *actual_type_name = my_get.get_type_name;
+ } else {
+ *results = valueset_dup(my_get.get_present_values);
+ if (NULL == *results) {
+ rc = ENOMEM;
+ } else {
+ *actual_type_name = slapi_ch_strdup(my_get.get_type_name);
+ if (NULL == *actual_type_name) {
+ rc = ENOMEM;
+ }
+ }
+ }
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_POINTERS;
+ } else {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+ }
+ } else {
+ rc = SLAPI_VIRTUALATTRS_NOT_FOUND;
+ }
+ }
+ if (!use_local_ctx) {
+ vattr_context_ungrok(&c);
+ }
+ return rc;
+}
+
+/*
+ *
+ * returns 0: (no_error && type found ) in which case:
+ * results: contains the current values for type and
+ * all it's subtypes in e
+ * type_name_disposition: how each type was matched
+ * SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS
+ * SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE
+ * actual_type_name: type name as found
+ * flags: bit mask of options:
+ * SLAPI_REALATTRS_ONLY
+ * SLAPI_VIRTUALATTRS_ONLY
+ * SLAPI_VIRTUALATTRS_REQUEST_POINTERS
+ * SLAPI_VIRTUALATTRS_LIST_OPERATIONAL_ATTRS
+ * buffer_flags: bit mask to be used as input flags for
+ * slapi_values_free()
+ * SLAPI_VIRTUALATTRS_RETURNED_POINTERS
+ * SLAPI_VIRTUALATTRS_RETURNED_COPIES
+ * SLAPI_VIRTUALATTRS_REALATTRS_ONLY
+ * item_count: number of subtypes matched
+ * otherwise:
+ * SLAPI_VIRTUALATTRS_LOOP_DETECTED (failed to eval a vattr)
+ * SLAPI_VIRTUALATTRS_NOT_FOUND (type not recognised by any vattr
+ * sp && not a real attr in entry )
+ * ENOMEM (memory error)
+ *
+ * Note:
+ * . modifes the virtual cache in the entry.
+ * . for cached vattrs you always get a copy, so it will need to be
+ * freed via slapi_values_free().
+ *
+ */
+
+int slapi_vattr_values_get_sp_ex(vattr_context *c,
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* attr type name */ char *type,
+ /* pointer to result set */ Slapi_ValueSet*** results,
+ int **type_name_disposition,
+ char*** actual_type_name, int flags,
+ int *buffer_flags, int *item_count)
+{
+ return slapi_vattr_namespace_values_get_sp(
+ c, e, NULL, type, results, type_name_disposition,
+ actual_type_name, flags, buffer_flags, item_count
+ );
+}
+
+int slapi_vattr_namespace_values_get_sp(vattr_context *c,
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* DN denoting backend namespace */ Slapi_DN *namespace_dn,
+ /* attr type name */ char *type,
+ /* pointer to result set */ Slapi_ValueSet*** results,
+ int **type_name_disposition,
+ char*** actual_type_name, int flags,
+ int *buffer_flags, int *item_count)
+{
+
+ int rc = 0;
+ int sp_bit = 0; /* Set if an SP supplied an answer */
+ vattr_sp_handle_list *list = NULL;
+ vattr_get_thang *my_get = NULL;
+ int attr_count = 0;
+
+ rc = vattr_context_grok(&c);
+ if (0 != rc) {
+ /* Print a handy error log message */
+ if(!vattr_context_is_loop_msg_displayed(&c))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,"Detected virtual attribute loop in get on entry %s, attribute %s\n", slapi_entry_get_dn_const(e), type, 0);
+ vattr_context_set_loop_msg_displayed(&c);
+ }
+ return rc;
+ }
+
+ /* Having done that, we now consult the attribute map to find service providers who are interested */
+ /* Look for attribute in the map */
+ if(!(flags & SLAPI_REALATTRS_ONLY))
+ {
+ /* we use the vattr namespace aware version of this */
+ list = vattr_map_namespace_sp_getlist(namespace_dn, type);
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+ void *hint = NULL;
+ Slapi_Attr* cache_attr = 0;
+ char *vattr_type=NULL;
+ /* first lets consult the cache to save work */
+ int cache_status;
+
+ cache_status =
+ slapi_entry_vattrcache_find_values_and_type_ex(e, type,
+ results,
+ actual_type_name);
+ switch(cache_status)
+ {
+ case SLAPI_ENTRY_VATTR_RESOLVED_EXISTS: /* cached vattr */
+ {
+ sp_bit = 1;
+
+ /* Complete analysis of type matching */
+ *type_name_disposition =
+ (int *)slapi_ch_malloc(sizeof(*type_name_disposition));
+ if ( 0 == slapi_attr_type_cmp( type , **actual_type_name, SLAPI_TYPE_CMP_EXACT) )
+ {
+ **type_name_disposition =
+ SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ } else {
+ **type_name_disposition =
+ SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE;
+ }
+
+ *item_count = 1;
+
+ break;
+ }
+
+ case SLAPI_ENTRY_VATTR_RESOLVED_ABSENT: /* does not exist */
+ break; /* look in entry */
+
+ case SLAPI_ENTRY_VATTR_NOT_RESOLVED: /* not resolved */
+ default: /* any other result, resolve */
+ {
+ /* bit cacky, but need to make a null terminated lists for now
+ * for the (unimplemented and so fake) batch attribute request
+ */
+ char **type_list = (char**)slapi_ch_calloc(2,sizeof(char*));
+ void **hint_list = (void**)slapi_ch_calloc(2,sizeof(void*));
+
+ type_list[0] = type;
+
+ for (current_handle = vattr_map_sp_first(list,&hint); current_handle; current_handle = vattr_map_sp_next(current_handle,&hint)) {
+ /* call this SP */
+
+ hint_list[0] = hint;
+
+ rc = vattr_call_sp_get_batch_values(current_handle,c,e,my_get,
+ type_list,results,type_name_disposition,
+ actual_type_name, flags,buffer_flags, hint_list);
+ if (0 == rc) {
+ sp_bit = 1; /* this sp provided an answer, we are done */
+
+ /* count the items in the (null terminated) result list */
+ *item_count = 0;
+
+ while((*results)[*item_count])
+ {
+ (*item_count)++;
+ }
+
+ break;
+ }
+ }
+
+ slapi_ch_free((void**)&type_list);
+ slapi_ch_free((void**)&hint_list);
+
+ if(!sp_bit)
+ {
+ /* we have failed and must now examine the entry itself
+ *
+ * But first lets cache the no result
+ * dups the type (if necessary).
+ */
+ slapi_entry_vattrcache_merge_sv(e, type, NULL );
+
+ }
+ else
+ {
+ /*
+ * we need to cache the virtual attribute
+ * dups the type (if necessary) and results.
+ */
+ /*
+ * this (and above) will of course need to get updated
+ * when we do real batched attributes
+ */
+ slapi_entry_vattrcache_merge_sv(e, **actual_type_name,
+ **results );
+ }
+ }
+ }
+ }
+ }
+
+ /* If no SP supplied the answer, take it from the entry */
+ if (!sp_bit && !(flags & SLAPI_VIRTUALATTRS_ONLY))
+ {
+ int counter;
+
+ /* First grok the entry - allocates memory for list */
+ attr_count = vattr_helper_get_entry_conts_ex(e,type, &my_get,
+ (0 != (flags & SLAPI_VIRTUALATTRS_SUPPRESS_SUBTYPES)));
+ *item_count = attr_count;
+ rc = 0; /* reset return code (cause an sp must have failed) */
+
+ if(attr_count > 0)
+ {
+
+ *results = (Slapi_ValueSet**)slapi_ch_calloc(1, sizeof(*results) * attr_count);
+ *type_name_disposition = (int *)slapi_ch_malloc(sizeof(*type_name_disposition) * attr_count);
+ *actual_type_name = (char**)slapi_ch_malloc(sizeof(*actual_type_name) * attr_count);
+
+ /* For attributes which are in the entry, we just need to get to the Slapi_Attr structure and yank out the slapi_value_set
+ structure. We either return a pointer directly to it, or we copy it, depending upon whether the caller asked us to try to
+ avoid copying.
+ */
+ for(counter = 0; counter < attr_count; counter++)
+ {
+ (*type_name_disposition)[counter] = my_get[counter].get_name_disposition;
+
+ if (my_get[counter].get_present) {
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ /* pointers will do, good */
+ (*results)[counter] = my_get[counter].get_present_values;
+ (*actual_type_name)[counter] = my_get[counter].get_type_name;
+ } else {
+ /* need to copy the values */
+ (*results)[counter] = valueset_dup(my_get[counter].get_present_values);
+ if (NULL == (*results)[counter]) {
+ rc = ENOMEM;
+ } else {
+ (*actual_type_name)[counter] = slapi_ch_strdup(my_get[counter].get_type_name);
+ if (NULL == (*actual_type_name)[counter]) {
+ rc = ENOMEM;
+ }
+ }
+ }
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_POINTERS;
+ } else {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+ }
+ } else {
+ rc = SLAPI_VIRTUALATTRS_NOT_FOUND;
+ }
+ }
+
+ slapi_ch_free((void **)&my_get);
+ }
+ }
+ vattr_context_ungrok(&c);
+ return rc;
+}
+
+/* Do we need a call to free the results from the above ? */
+
+void slapi_vattr_values_free(Slapi_ValueSet **value, char** actual_type_name,
+ int flags)
+{
+ /* Check whether we need to free the strings */
+ if (flags & SLAPI_VIRTUALATTRS_RETURNED_POINTERS) {
+ /* We don't need to free the values */
+ } else {
+ /* Free the valueset */
+ if (*value) {
+ slapi_valueset_free(*value);
+ }
+ if (*actual_type_name) {
+ slapi_ch_free((void **)actual_type_name);
+ }
+ }
+ *actual_type_name = NULL;
+ *value = NULL;
+}
+
+/* Function like the one above but doing a compare operation */
+/* Same return codes as above. Compare result value returned in "result": 0 if compare true, 1 if compare false */
+
+int slapi_vattr_value_compare(Slapi_Entry *e, char *type, Slapi_Value *test_this, int *result, int flags)
+{
+ return slapi_vattr_namespace_value_compare_sp(NULL,e,NULL,type,test_this,result,flags);
+}
+
+/* namespace version of above */
+int slapi_vattr_namespace_value_compare(Slapi_Entry *e, Slapi_DN *namespace_dn, const char *type, Slapi_Value *test_this, int *result, int flags)
+{
+ return slapi_vattr_namespace_value_compare_sp(NULL,e,namespace_dn,type,test_this,result,flags);
+}
+
+int slapi_vattr_value_compare_sp(vattr_context *c,/* Entry we're interested in */ Slapi_Entry *e,/* attr type name */ char *type, Slapi_Value *test_this,/* pointer to result */ int *result, int flags)
+{
+ return slapi_vattr_namespace_value_compare_sp(c,e,NULL,type,test_this,result,flags);
+}
+
+int slapi_vattr_namespace_value_compare_sp(vattr_context *c,/* Entry we're interested in */ Slapi_Entry *e, /* backend namespace dn*/Slapi_DN *namespace_dn, /* attr type name */ const char *type, Slapi_Value *test_this,/* pointer to result */ int *result, int flags)
+{
+ int rc = 0;
+
+ int sp_bit = 0; /* Set if an SP supplied an answer */
+ vattr_sp_handle_list *list = NULL;
+
+ vattr_get_thang *my_get = 0;
+
+ *result = 0; /* return "compare false" by default */
+
+ rc = vattr_context_grok(&c);
+ if (0 != rc) {
+ /* Print a handy error log message */
+ LDAPDebug(LDAP_DEBUG_ANY,"Detected virtual attribute loop in compare on entry %s, attribute %s\n", slapi_entry_get_dn_const(e), type, 0);
+ return rc;
+ }
+
+ /* Having done that, we now consult the attribute map to find service providers who are interested */
+ /* Look for attribute in the map */
+ list = vattr_map_namespace_sp_getlist(namespace_dn, type);
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+ void *hint = NULL;
+ for (current_handle = vattr_map_sp_first(list,&hint); current_handle; current_handle = vattr_map_sp_next(list,&hint)) {
+ /* call this SP */
+ rc = vattr_call_sp_compare_value(current_handle,c,e,my_get,type,test_this,result,flags, hint);
+ if (0 == rc) {
+ sp_bit = 1;
+ break;
+ }
+ }
+ }
+ /* If no SP supplied the answer, take it from the entry */
+ if (!sp_bit) {
+ /* Grok the entry, and remember what we saw. This call does no more than walk down the entry attribute list, do some string compares and copy pointers. */
+ int attr_count = vattr_helper_get_entry_conts_ex(e,type, &my_get,
+ (0 != (flags & SLAPI_VIRTUALATTRS_SUPPRESS_SUBTYPES)));
+
+ if (my_get && my_get->get_present) {
+ int i;
+ Slapi_Value *Dummy_value = NULL;
+
+ /* Put the required stuff in the fake attr */
+
+ for(i=0;i<attr_count;i++)
+ {
+ Dummy_value = slapi_valueset_find( my_get[i].get_attr, my_get[i].get_present_values, test_this );
+
+ if (Dummy_value) {
+ *result = 1; /* return "compare true" */
+
+ break;
+ }
+ }
+ } else {
+ rc = SLAPI_VIRTUALATTRS_NOT_FOUND;
+ }
+ }
+ vattr_context_ungrok(&c);
+ slapi_ch_free((void **)&my_get);
+ return rc;
+}
+
+/* Function to obtain the list of attribute types which are available on this entry */
+
+/*
+ Need to call service providers here:
+ We know only the entry's DN and so we could restrict our choice of SPs based on that.
+ However, we don't yet implement such fancyness.
+ For now, we just crawl through all the SPs, asking them in turn to contribute.
+ This is pretty inefficient because we will trawl the list for each wildcard attribute type search operation.
+ Service providers should therefore handle the call as fast as they can.
+ */
+
+struct _vattr_type_list_context {
+ unsigned int vattr_context_loop_count;
+ vattr_type_thang *types;
+ int realattrs_only; /* TRUE implies list contains only real attrs */
+ int list_is_copies;
+ int flags;
+ size_t list_length;
+ size_t block_length;
+};
+
+/* Helper function which converts a type list from pointers to copies */
+static int vattr_convert_type_list(vattr_type_list_context *thang)
+{
+ vattr_type_thang *old_list = NULL;
+ vattr_type_thang *new_list = NULL;
+ size_t index = 0;
+
+ old_list = thang->types;
+ /* Make a new list */
+ new_list = (vattr_type_thang*)slapi_ch_calloc(thang->block_length, sizeof(vattr_type_thang));
+ if (NULL == new_list) {
+ return ENOMEM;
+ }
+ /* Walk the list strdup'ing the type name and copying the rest of the structure contents */
+ for (index = 0; index < thang->list_length; index++) {
+ new_list[index].type_flags = old_list[index].type_flags;
+ new_list[index].type_name = slapi_ch_strdup(old_list[index].type_name);
+ /*
+ * list_is_copies does not affect type_values as it
+ * is always a pointer never a copy.
+ */
+ new_list[index].type_values = old_list[index].type_values;
+ }
+ /* Mark it a copy list */
+ thang->list_is_copies = 1;
+ /* Free the original list */
+ slapi_ch_free((void **)&(thang->types));
+ /* swap lists */
+ thang->types = new_list;
+
+ return 0;
+}
+
+/*
+ * Returns all the attribute types from e, both real and virtual.
+ *
+ * Any of the attributes found in the entry to start with, for
+ * whom no vattr SP volunteered, will also have a pointer to the
+ * original valueset in the entry--this fact can be used by
+ * calling slapi_vattr_values_type_thang_get(), which will
+ * take the values present in the vattr_type_thang list
+ * rather than calling slapi_vattr_values_get() to retrieve the value.
+*/
+
+#define TYPE_LIST_EXTRA_SPACE 5 /* Number of extra slots we allow for SP's to insert types */
+
+int slapi_vattr_list_attrs(/* Entry we're interested in */ Slapi_Entry *e,
+ /* pointer to receive the list */ vattr_type_thang **types,
+ int flags, int *buffer_flags)
+{
+ int rc = 0;
+ size_t attr_count = 0;
+ vattr_type_thang *result_array = NULL ;
+ Slapi_Attr *current_attr = NULL;
+ size_t i = 0;
+ int list_is_copies = 0;
+ vattr_sp_handle_list *list = NULL;
+ size_t list_length = 0;
+ size_t block_length = 0;
+ vattr_type_list_context type_context = {0};
+
+ block_length = 1 + TYPE_LIST_EXTRA_SPACE;
+
+ if(!(flags & SLAPI_VIRTUALATTRS_ONLY))
+ {
+ /* First find what's in the entry itself*/
+ /* Count the attributes */
+ for (current_attr = e->e_attrs; current_attr != NULL; current_attr = current_attr->a_next, attr_count++) ;
+ block_length += attr_count;
+ /* Allocate the pointer array */
+ result_array = (vattr_type_thang*)slapi_ch_calloc(block_length,sizeof(vattr_type_thang));
+ if (NULL == result_array) {
+ return ENOMEM;
+ }
+ list_length = attr_count;
+
+ /* Now copy the pointers into the array */
+ i = 0;
+ for (current_attr = e->e_attrs; current_attr != NULL; current_attr = current_attr->a_next) {
+ char *mypointer = NULL;
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ mypointer = current_attr->a_type;
+ result_array[i].type_values = &(current_attr->a_present_values);
+ } else {
+ mypointer = slapi_ch_strdup(current_attr->a_type);
+ list_is_copies = 1;
+ }
+ result_array[i].type_flags = current_attr->a_flags;
+ result_array[i++].type_name = mypointer;
+ }
+ PR_ASSERT(i == attr_count);
+ }
+
+ /* if we didnt send real attrs we need to allocate result array */
+ if(NULL == result_array)
+ {
+ result_array = (vattr_type_thang*)slapi_ch_calloc(block_length,sizeof(vattr_type_thang));
+ if (NULL == result_array) {
+ return ENOMEM;
+ }
+ }
+
+ /* Then ask the service providers for their input */
+ type_context.flags = flags;
+ type_context.realattrs_only = 1; /* until we know otherwise */
+ type_context.list_is_copies = list_is_copies;
+ type_context.types = result_array;
+ type_context.list_length = list_length;
+ type_context.block_length = block_length;
+
+ /*
+ * At this point type_context.types is a list of all
+ * the attributetypes (copies or pointers) and values (copies, if present)
+ * found in the entry.
+ */
+
+ if(!(flags & SLAPI_REALATTRS_ONLY))
+ {
+ list = vattr_map_sp_get_complete_list();
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+
+ for (current_handle = vattr_list_sp_first(list); current_handle; current_handle = vattr_list_sp_next((vattr_sp_handle_list *)current_handle)) {
+ /* call this SP */
+ rc = vattr_call_sp_get_types(current_handle,e,&type_context,
+ flags);
+ if (0 != rc) {
+ /* DBDB do what on error condition ? */
+ }
+ }
+ /* assert enough space for the null terminator */
+ PR_ASSERT( type_context.list_length < type_context.block_length);
+ i = type_context.list_length;
+ }
+ }
+
+ /*
+ * Now type_context.types is a list of all the types in this entry--
+ * real and virtual. For real ones, the values field is filled in.
+ * For virtual ones (including virtual ones which will overwrite a real
+ * value) the values field is null--this fact may used
+ * subsequently by callers of slapi_vattr_list_attrs()
+ * by calling slapi_vattr_values_type_thang_get() which
+ * will only calculate the values for attributetypes with
+ * non-null values.
+ */
+
+ flags = type_context.flags;
+ list_is_copies = type_context.list_is_copies;
+ result_array = type_context.types;
+ list_length = type_context.list_length;
+ block_length = type_context.block_length;
+
+ if (list_is_copies) {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+ } else {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_POINTERS;
+ }
+
+ /* Let the caller know if the list contains only real attrs */
+ if (type_context.realattrs_only) {
+ *buffer_flags |= SLAPI_VIRTUALATTRS_REALATTRS_ONLY;
+ }
+
+ result_array[i].type_name = NULL;
+
+ if (types && list_length) {
+ *types = result_array;
+ }
+ else
+ *types = 0;
+
+ return rc;
+}
+
+static int vattr_add_thang_to_list(vattr_type_list_context *c, vattr_type_thang *thang)
+{
+ vattr_type_thang *new_list = NULL;
+
+ /* Will it fit in the current block ? */
+ if (c->list_length == c->block_length - 1) {
+ c->block_length *= 2;
+
+ new_list = (vattr_type_thang*)slapi_ch_realloc((char *)c->types,
+ c->block_length * (int)sizeof(vattr_type_thang));
+ if (NULL == new_list) {
+ return ENOMEM;
+ }
+
+ c->types = new_list;
+ }
+ /* Add to the end of the list */
+ c->types[c->list_length ] = *thang;
+ c->list_length += 1;
+ return 0;
+}
+
+/* Called by SP's during their get_types call, to have the server add a
+ * type to the type list.
+ *
+ * c: the vattr_type_list_context passed from the original caller of
+ * slapi_vattr_list_attrs()
+ * thang: the new type to be added to the type list.
+ * flags: tells the routine whether to copy the thang or not.
+ * SLAPI_VIRTUALATTRS_REQUEST_POINTERS--no need to copy it.
+ * !(SLAPI_VIRTUALATTRS_REQUEST_POINTERS)--need to copy it.
+ *
+ * Checks to see if the requested type is already in the list,
+ * and if it is, if the value of the attribute is
+ * non-null, it resets the value to null. This means that when used
+ * subsequently, the type list indicates whether, for a given attribute type,
+ * the SP's need to be called to retrieve the value.
+ *
+*/
+int slapi_vattrspi_add_type(vattr_type_list_context *c,
+ vattr_type_thang *thang, int flags)
+{
+ int rc = 0;
+ unsigned int index = 0;
+ vattr_type_thang thang_to_add = {0};
+
+ int found_it = 0;
+
+ PR_ASSERT(c);
+
+ /* We are no longer sure that the list contains only real attrs */
+ c->realattrs_only = 0;
+
+ /* Check to see if the type is in the list already */
+ for (index = 0; index < c->list_length; index++) {
+ if (0 == slapi_UTF8CASECMP(c->types[index].type_name,thang->type_name)) {
+ found_it = 1;
+ break;
+ }
+ }
+ /*
+ * If found and there are values specified in the vattr_type_thang, then
+ * that means it's a real attribute--because SP's do not add values
+ * when called via slapi_vattr_list_attrs()/vattr_call_sp_get_types().
+ * However, an SP is willing to claim responsibility for this attribute
+ * type, so to ensure that that virtual value will get calculated, set the
+ * original real value to NULL here.
+ * The guiding rule here is "if an SP is prepared
+ * to provide a value for an attribute type, then that is the one
+ * that must be returned to the user". Note, this does not prevent
+ * the SP implementing a "merge real with virtual policy", if they
+ * wish.
+ */
+
+ if (found_it) {
+
+ if ( c->types[index].type_values != NULL ) {
+ /*
+ * Any values in this list are always pointers.
+ * See slapi_vattr_list_types() and vattr_convert_type_list()
+ */
+ c->types[index].type_values = NULL;
+ }
+
+ return 0;
+ }
+ /*
+ * If it's not in the list then we need to add it.
+ * If the list is already copies, then we need to make a copy of what the
+ * SP passed us.
+ * If the SP indicated that the type name it passed us is volatile,
+ * !(flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS)
+ * then we need to copy it anyway.
+ */
+
+ /* rbyrneXXX optimisation for COS: if each type_thang could have a
+ * free_flag
+ * then we would not have to convert all the real attrs to copies
+ * just because an SP returned a copy (note: only COS rrequires copies;
+ * roles returns a pointer to a static string "nsRole").
+ */
+ if (!c->list_is_copies &&
+ (0 == (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS))) {
+ /* Means we need to turn the existing list into a list of copies */
+ vattr_convert_type_list(c);
+ }
+ if (c->list_is_copies) {
+ thang_to_add.type_flags = thang->type_flags;
+ thang_to_add.type_name = slapi_ch_strdup(thang->type_name);
+ } else {
+ thang_to_add = *thang;
+ }
+ /* Lastly, we add to the list, using a function which hides the complexity of extending the list if it fills up */
+ vattr_add_thang_to_list(c, &thang_to_add);
+
+ return rc;
+}
+
+/* Function to free the list returned in the function above */
+/* Written, reviewed, debug stepped */
+
+void slapi_vattr_attrs_free(vattr_type_thang **types, int flags)
+{
+ if (NULL == *types) {
+ return;
+ }
+ /* Check whether we need to free the strings */
+ if (flags & SLAPI_VIRTUALATTRS_RETURNED_POINTERS) {
+ /* We don't need to free the values */
+ } else {
+ char *attr_name_to_free = NULL;
+ size_t i = 0;
+ /* Walk down the set of values, fr eeing each one in turn */
+ for (; (attr_name_to_free = (*types)[i].type_name) != NULL; i++) {
+ slapi_ch_free((void **)&attr_name_to_free);
+ }
+ }
+ /* We always need to free the pointer block */
+ slapi_ch_free((void **)types);
+}
+
+char *vattr_typethang_get_name(vattr_type_thang *t)
+{
+ return t->type_name;
+}
+
+unsigned long vattr_typethang_get_flags(vattr_type_thang *t)
+{
+ return t->type_flags;
+}
+
+vattr_type_thang *vattr_typethang_next(vattr_type_thang *t)
+{
+ t++;
+ if (t->type_name) {
+ return t;
+ } else {
+ return NULL;
+ }
+}
+
+vattr_type_thang *vattr_typethang_first(vattr_type_thang *t)
+{
+ return t;
+}
+
+vattr_get_thang *slapi_vattr_getthang_first(vattr_get_thang *t)
+{
+ return t;
+}
+
+vattr_get_thang *slapi_vattr_getthang_next(vattr_get_thang *t)
+{
+ t++;
+ if (t->get_present) {
+ return t;
+ } else {
+ return NULL;
+ }
+}
+
+
+
+
+/* End of the public interface functions */
+
+/* Everything below here is the SPI interface, only callable by vattr service providers.
+ Currently this interface is not public. Interface definitions are in vattr_spi.h
+ */
+
+/* Structures used in the SPI interface */
+
+/* Service provider object */
+
+
+struct _vattr_sp {
+ /* vtbl */
+ vattr_get_fn_type sp_get_fn;
+ vattr_get_ex_fn_type sp_get_ex_fn;
+ vattr_compare_fn_type sp_compare_fn;
+ vattr_types_fn_type sp_types_fn;
+ void *sp_data; /* Pointer for use by the Service Provider */
+};
+typedef struct _vattr_sp vattr_sp;
+
+/* Service provider handle */
+struct _vattr_sp_handle {
+ vattr_sp *sp;
+ struct _vattr_sp_handle *next; /* So we can link them together in the map */
+ void *hint; /* Hint to the SP */
+};
+
+/* Calls made by Service Providers */
+/* Call to register as a service provider */
+/* This function needs to do the following:
+ o Let the provider say "hey, I'm here". It should probably get some handle back which it can use in future calls.
+ o Say whether it wants to be called to resolve every single query, or whether it will say in advance what attrs it services.
+ */
+
+static vattr_sp_handle *vattr_sp_list = NULL;
+
+int slapi_vattrspi_register(vattr_sp_handle **h, vattr_get_fn_type get_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn)
+{
+ return slapi_vattrspi_register_internal(h, get_fn, 0, compare_fn, types_fn, 0);
+}
+
+int slapi_vattrspi_register_ex(vattr_sp_handle **h, vattr_get_ex_fn_type get_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn, void *options)
+{
+ return slapi_vattrspi_register_internal(h, 0, get_fn, compare_fn, types_fn, options);
+}
+
+/* options not used yet - for future expansion */
+int slapi_vattrspi_register_internal(vattr_sp_handle **h, vattr_get_fn_type get_fn, vattr_get_ex_fn_type get_ex_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn, void *options)
+{
+ vattr_sp_handle *return_to_caller = NULL;
+ vattr_sp_handle *list_handle = NULL;
+ vattr_sp *new_sp = NULL;
+ /* Make a service provider handle */
+ new_sp = (vattr_sp*)slapi_ch_calloc(1,sizeof(vattr_sp));
+ if (NULL == new_sp) {
+ slapd_nasty(sourcefile,7,0);
+ return ENOMEM;
+ }
+ return_to_caller = (vattr_sp_handle*)slapi_ch_calloc(1,sizeof(vattr_sp_handle));
+ if (NULL == return_to_caller) {
+ slapd_nasty(sourcefile,8,0);
+ return ENOMEM;
+ }
+ new_sp->sp_get_fn = get_fn;
+ new_sp->sp_get_ex_fn = get_ex_fn;
+ new_sp->sp_compare_fn = compare_fn;
+ new_sp->sp_types_fn = types_fn;
+ return_to_caller->sp = new_sp;
+ /* Add to the service provider list */
+ /* Make a handle for the list */
+ list_handle = (vattr_sp_handle*)slapi_ch_calloc(1, sizeof (vattr_sp_handle));
+ if (NULL == list_handle) {
+ return ENOMEM;
+ }
+ *list_handle = *return_to_caller;
+ list_handle->next = vattr_sp_list;
+ vattr_sp_list = list_handle;
+ /* Return the handle to the caller */
+ *h = return_to_caller;
+ return 0;
+}
+
+vattr_sp_handle_list *vattr_map_sp_get_complete_list()
+{
+ return vattr_sp_list;
+}
+
+int slapi_vattrspi_regattr(vattr_sp_handle *h,char *type_name_to_register, char* DN , void *hint)
+{
+ int ret = 0;
+ char *type_to_add;
+ int free_type_to_add = 0;
+
+ /* Supplying a DN means that the plugin requires to be called
+ * only when the considering attributes in relevant entries - almost
+ *
+ * Actually the smallest namespace is the backend.
+ *
+ * atttributes that are tied to backends have the format DN::attr
+ * this essentially makes the attribute have split namespaces
+ * that are treated as separate attributes in the vattr code
+ */
+ if(DN)
+ {
+ /* as a coutesy we accept any old DN and will convert
+ * to a namespace DN, this helps to hide details
+ * (that we might decide to change) anyway
+ */
+ Slapi_DN original_dn;
+ Slapi_Backend *be;
+ Slapi_DN *namespace_dn;
+
+ slapi_sdn_init(&original_dn);
+ slapi_sdn_set_dn_byref(&original_dn,DN);
+ be = slapi_be_select( &original_dn );
+ namespace_dn = (Slapi_DN*)slapi_be_getsuffix(be, 0);
+
+ if(namespace_dn && be != defbackend_get_backend()) /* just in case someone thinks "" is a good namespace */
+ {
+ type_to_add = (char*)PR_smprintf("%s::%s",
+ (char*)slapi_sdn_get_dn(namespace_dn),
+ type_name_to_register);
+
+ if(!type_to_add)
+ {
+ ret = -1;
+ }
+
+ free_type_to_add = 1;
+ }
+ else
+ {
+ type_to_add = type_name_to_register;
+ }
+
+ slapi_sdn_done(&original_dn);
+ }
+ else
+ {
+ type_to_add = type_name_to_register;
+ }
+
+ ret = vattr_map_sp_insert(type_to_add,h,hint);
+
+ if(free_type_to_add)
+ {
+ PR_smprintf_free(type_to_add);
+ }
+
+ return ret;
+}
+
+
+/* Call to advertise or refute that you generate the value for some attribute, and to signal that the definition for this attribute has changed */
+/* This call needs to do the following:
+ o Let the SP say "you know, I will generate the value of attribute "foo", within subtree "bar". (we might ignore the subtree for now to keep things simple
+ o Same as above, but say "you know, I'm not doing that any more". (we'll do that with a addordel flag).
+ o Say, "you know that definition I said I did, well it's changed".
+*/
+
+/* Functions to handle the context stucture */
+
+int slapi_vattr_context_create(vattr_context **c)
+{
+ return 0;
+}
+
+void slapi_vattr_context_destroy(vattr_context *c)
+{
+}
+
+int vattr_call_sp_get_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *buffer_flags, void* hint)
+{
+ int ret = -1;
+
+ if(handle->sp->sp_get_fn)
+ {
+ ret = ((handle->sp->sp_get_fn)(handle,c,e,type,results,type_name_disposition,actual_type_name,flags,buffer_flags, hint));
+ }
+
+ return ret;
+}
+
+int vattr_call_sp_get_batch_values(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, char **type, Slapi_ValueSet*** results,int **type_name_disposition, char*** actual_type_name, int flags, int *buffer_flags, void** hint)
+{
+ int ret = 0;
+
+ if(handle->sp->sp_get_ex_fn)
+ {
+ ret = ((handle->sp->sp_get_ex_fn)(handle,c,e,type,results,type_name_disposition,actual_type_name,flags,buffer_flags, hint));
+ }
+ else
+ {
+ /* make our args look like the simple non-batched case */
+ *results = (Slapi_ValueSet**)slapi_ch_calloc(2, sizeof(*results)); /* 2 for null terminated list */
+ *type_name_disposition = (int *)slapi_ch_calloc(2, sizeof(*type_name_disposition));
+ *actual_type_name = (char**)slapi_ch_calloc(2, sizeof(*actual_type_name));
+
+ ret =((handle->sp->sp_get_fn)(handle,c,e,*type,*results,*type_name_disposition,*actual_type_name,flags,buffer_flags, hint));
+ if(ret)
+ {
+ slapi_ch_free((void**)results );
+ slapi_ch_free((void**)type_name_disposition );
+ slapi_ch_free((void**)actual_type_name );
+ }
+ }
+
+ return ret;
+}
+
+int vattr_call_sp_compare_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, const char *type, Slapi_Value* test_this,int *result, int flags, void* hint)
+{
+ return ((handle->sp->sp_compare_fn)(handle,c,e,(char*)type,test_this,result,flags, hint));
+}
+
+int vattr_call_sp_get_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags)
+{
+ return ((handle->sp->sp_types_fn)(handle,e,type_context,flags));
+}
+
+/* Service provider entry point prototypes */
+
+/* Call which allows a SP to say what attributes can be used to define a given attribute, used for loop detection */
+/* Not implementing this yet */
+/* typedef int (*vattrspi_explain_definition) (); */
+
+/* Function called to indicate to the SP that the game is over, we free the handle, not the SP */
+typedef int (*vattrspi_deregister) (vattr_sp_handle *h);
+
+/* End of the SPI functions */
+
+/* Implementation functions only after here */
+
+/* The attribute map */
+
+/* We need a structure which can map attribute type names to SP's which might tell us
+something useful. We need a function which, given an attr type name, can return
+a set of SP's to ask about said attr. SP's which don't tell us which attrs they are
+handling always get asked. The first SP which tells us the answer wins, we don't
+attempt to arbitrate between them. One might imaging doing this stuff in a unified
+way with other stuff pertaining to types (schema, syntax). Someday. For now this
+is a separate thing in the insterests of stability.
+*/
+
+/* Virtual Attribute map implementation */
+
+/* The virtual attribute type map is implemented as a hash table.
+ The single key is the attribute type base name (not an alias)
+ (what about subtypes??). The hash table takes care of translating
+ this key to a list of probable service providers for the type.
+
+ Other than the obvious lookup function, we need functions to
+ allow entries to be added and removed from the map.
+
+ Because the map is likely to be consulted at least once for every search
+ operation performed by the server, it's important that it is free from
+ performance deficiencies such as lock contention (is the NSPR hash table
+ implementation even thread safe ?
+
+ */
+
+#define VARRT_MAP_HASHTABLE_SIZE 10
+
+/* Attribute map oject */
+/* Needs to contain: a linked list of pointers to provider handles handles,
+ The type name,
+ other stuff ?
+
+ Access to the entire map will be controlled via a single RW lock.
+ */
+
+struct _objAttrValue
+{
+ struct _objAttrValue *pNext;
+ Slapi_Value *val;
+};
+typedef struct _objAttrValue objAttrValue;
+
+struct _vattr_map_entry {
+ char * /* currect ? */ type_name;
+ vattr_sp_handle *sp_list;
+ objAttrValue *objectclasses; /* objectclasses for this type to check schema with*/
+};
+typedef struct _vattr_map_entry vattr_map_entry;
+
+
+vattr_map_entry test_entry = {NULL};
+
+struct _vattr_map {
+ PRRWLock *lock;
+ PLHashTable *hashtable; /* Hash table */
+};
+typedef struct _vattr_map vattr_map;
+
+static vattr_map *the_map = NULL;
+
+static PRIntn vattr_hash_compare_keys(const void *v1, const void *v2)
+{
+ /* Should this be subtype aware, etc ? */
+ return ( (0 == strcasecmp(v1,v2)) ? 1 : 0) ;
+}
+
+static PRIntn vattr_hash_compare_values(const void *v1, const void *v2)
+{
+ return( ((char *)v1 == (char *)v2 ) ? 1 : 0);
+}
+
+static PLHashNumber vattr_hash_fn(const void *type_name)
+{
+ /* need a hash function here */
+ /* Sum the bytes for now */
+ PLHashNumber result = 0;
+ char * current_position = NULL;
+ char current_char = 0;
+ for (current_position = (char*) type_name; *current_position; current_position++) {
+ current_char = tolower(*current_position);
+ result += current_char;
+ }
+ return result;
+}
+
+static int vattr_map_create()
+{
+ the_map = (vattr_map*)slapi_ch_calloc(1, sizeof(vattr_map));
+ if (NULL == the_map) {
+ slapd_nasty(sourcefile,1,0);
+ return ENOMEM;
+ }
+
+ the_map->hashtable = PL_NewHashTable(VARRT_MAP_HASHTABLE_SIZE,
+ vattr_hash_fn, vattr_hash_compare_keys,
+ vattr_hash_compare_values, NULL, NULL);
+
+ if (NULL == the_map->hashtable) {
+ slapd_nasty(sourcefile,2,0);
+ return ENOMEM;
+ }
+
+ the_map->lock = PR_NewRWLock(0,SOURCEFILE "1");
+ if (NULL == the_map) {
+ slapd_nasty(sourcefile,3,0);
+ return ENOMEM;
+ }
+ return 0;
+}
+
+static void vattr_map_destroy()
+{
+ if (the_map) {
+ if (the_map->hashtable) {
+ PL_HashTableDestroy(the_map->hashtable);
+ }
+ if (the_map->lock) {
+ PR_DestroyRWLock(the_map->lock);
+ }
+ }
+ slapi_ch_free ((void**)&the_map);
+}
+
+/* Returns 0 if present, entry returned in result. Returns SLAPI_VIRTUALATTRS_NOT_FOUND if not found */
+static int vattr_map_lookup(const char *type_to_find, vattr_map_entry **result)
+{
+ char *basetype = 0;
+ char *tmp = 0;
+ char buf[SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH];
+
+ *result = NULL;
+ PR_ASSERT(the_map);
+
+ /* vattrs need to support subtypes
+ * we insist that one service provider
+ * will support all subtypes for a
+ * given superior, hence we look for
+ * superiors only here
+ */
+
+ tmp = slapi_attr_basetype( type_to_find, buf, sizeof(buf));
+ if(tmp)
+ {
+ basetype = tmp;
+ }
+ else
+ {
+ basetype = buf;
+ }
+
+ /* Get the reader lock */
+ PR_RWLock_Rlock(the_map->lock);
+ *result = (vattr_map_entry*)PL_HashTableLookupConst(the_map->hashtable,
+ (void*)basetype);
+ /* Release ze lock */
+ PR_RWLock_Unlock(the_map->lock);
+
+ if(tmp)
+ {
+ slapi_ch_free_string(&tmp);
+ }
+
+ if (*result) {
+ return 0;
+ } else {
+ return SLAPI_VIRTUALATTRS_NOT_FOUND;
+ }
+}
+
+/* Insert an entry into the attribute map */
+int vattr_map_insert(vattr_map_entry *vae)
+{
+ char *copy_of_type_name = NULL;
+ PR_ASSERT(the_map);
+ copy_of_type_name = slapi_ch_strdup(vae->type_name);
+ if (NULL == copy_of_type_name) {
+ slapd_nasty(sourcefile,6,0);
+ return ENOMEM;
+ }
+ /* Get the writer lock */
+ PR_RWLock_Wlock(the_map->lock);
+ /* Insert the thing */
+ /* It's illegal to call this function if the entry is already there */
+ PR_ASSERT(NULL == PL_HashTableLookupConst(the_map->hashtable,(void*)copy_of_type_name));
+ PL_HashTableAdd(the_map->hashtable,(void*)copy_of_type_name,(void*)vae);
+ /* Unlock and we're done */
+ PR_RWLock_Unlock(the_map->lock);
+ return 0;
+}
+
+/*
+ vattr_delete_attrvals
+ ---------------------
+ deletes a value list
+*/
+void vattr_delete_attrvals(objAttrValue **attrval)
+{
+ objAttrValue *val = *attrval;
+
+ while(val)
+ {
+ objAttrValue *next = val->pNext;
+ slapi_value_free(&val->val);
+ slapi_ch_free((void**)&val);
+ val = next;
+ }
+}
+
+/*
+ vattr_add_attrval
+ -----------------
+ adds a value to an attribute value list
+*/
+int vattr_add_attrval(objAttrValue **attrval, char *val)
+{
+ int ret = 0;
+ objAttrValue *theVal;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> vattr_add_attrval\n",0,0,0);
+
+ /* create the attrvalue */
+ theVal = (objAttrValue*) slapi_ch_malloc(sizeof(objAttrValue));
+ if(theVal)
+ {
+ theVal->val = slapi_value_new_string(val);
+ if(theVal->val)
+ {
+ theVal->pNext = *attrval;
+ *attrval = theVal;
+ }
+ else
+ {
+ slapi_ch_free((void**)&theVal);
+ LDAPDebug( LDAP_DEBUG_ANY, "vattr_add_attrval: failed to allocate memory\n",0,0,0);
+ ret = -1;
+ }
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "vattr_add_attrval: failed to allocate memory\n",0,0,0);
+ ret = -1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- vattr_add_attrval\n",0,0,0);
+ return ret;
+}
+
+
+objAttrValue *vattr_map_entry_build_schema(char *type_name)
+{
+ objAttrValue *ret = 0;
+ struct objclass *oc;
+ int attr_index = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> vattr_map_entry_build_schema\n",0,0,0);
+
+ /* this is the first opportunity to register
+ * with the statechange api, our init function
+ * gets called prior to loading plugins, so it
+ * was not available then
+ */
+ if(!statechange_api)
+ {
+ /* grab statechange api - we never release this */
+ if(!slapi_apib_get_interface(StateChange_v1_0_GUID, &statechange_api))
+ {
+ /* register for schema changes via dn */
+ statechange_register(statechange_api, "vattr", "cn=schema", NULL, NULL, (notify_callback) schema_changed_callback);
+ }
+ }
+
+ if(!config_get_schemacheck())
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- vattr_map_entry_build_schema - schema off\n",0,0,0);
+ return 0;
+ }
+
+ oc_lock_read();
+ for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next )
+ {
+ char **pppAttrs[2];
+ int index;
+ int attrType = 0;
+ int oc_added = 0;
+
+ pppAttrs[0] = oc->oc_required;
+ pppAttrs[1] = oc->oc_allowed;
+
+ /* we need to check both required and allowed attributes */
+ while(!oc_added && attrType < 2)
+ {
+ if(pppAttrs[attrType])
+ {
+ index = 0;
+
+ while(pppAttrs[attrType][index])
+ {
+ if(!PL_strcasecmp(pppAttrs[attrType][index], type_name))
+ {
+ vattr_add_attrval(&ret, oc->oc_name);
+ oc_added = 1;
+ break;
+ }
+ index++;
+ }
+ }
+ attrType++;
+ }
+ }
+ oc_unlock();
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- vattr_map_entry_build_schema\n",0,0,0);
+ return ret;
+}
+
+static PRIntn vattr_map_entry_rebuild_schema(PLHashEntry *he, PRIntn i, void *arg)
+{
+ vattr_map_entry *entry = (vattr_map_entry *)(he->value);
+
+ if(entry->objectclasses)
+ vattr_delete_attrvals(&(entry->objectclasses));
+
+ entry->objectclasses = vattr_map_entry_build_schema(entry->type_name);
+
+ return HT_ENUMERATE_NEXT;
+}
+
+void schema_changed_callback(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data)
+{
+ /* Get the writer lock */
+ PR_RWLock_Wlock(the_map->lock);
+
+ /* go through the list */
+ PL_HashTableEnumerateEntries(the_map->hashtable, vattr_map_entry_rebuild_schema, 0);
+
+ /* Unlock and we're done */
+ PR_RWLock_Unlock(the_map->lock);
+}
+
+
+int slapi_vattr_schema_check_type(Slapi_Entry *e, char *type)
+{
+ int ret = 0;
+ vattr_map_entry *map_entry;
+
+ if(config_get_schemacheck())
+ {
+ Slapi_Attr *attr;
+
+ if(0 == slapi_entry_attr_find(e, "objectclass", &attr))
+ {
+ Slapi_ValueSet *vs;
+
+ if(0 == slapi_attr_get_valueset(attr, &vs))
+ {
+ objAttrValue *obj;
+
+ if(0 == vattr_map_lookup(type, &map_entry))
+ {
+ PR_RWLock_Rlock(the_map->lock);
+
+ obj = map_entry->objectclasses;
+
+ while(obj)
+ {
+ if(slapi_valueset_find(attr, vs, obj->val))
+ {
+ /* this entry has an objectclass
+ * that allows or requires the
+ * attribute type
+ */
+ ret = -1;
+ break;
+ }
+
+ obj = obj->pNext;
+ }
+
+ PR_RWLock_Unlock(the_map->lock);
+ }
+
+ slapi_valueset_free(vs);
+ }
+ }
+
+ }
+ else
+ ret = -1;
+
+ return ret;
+}
+
+vattr_map_entry *vattr_map_entry_new(char *type_name, vattr_sp_handle *sph, void* hint)
+{
+ vattr_map_entry *result = NULL;
+ vattr_sp_handle *sp_copy = NULL;
+ sp_copy = (vattr_sp_handle*)slapi_ch_calloc(1, sizeof (vattr_sp_handle));
+ if (!sp_copy) {
+ return NULL;
+ }
+ sp_copy->sp = sph->sp;
+ sp_copy->hint = hint;
+ result = (vattr_map_entry*)slapi_ch_calloc(1, sizeof (vattr_map_entry));
+ if (result) {
+ result->type_name = slapi_ch_strdup(type_name);
+ result->sp_list = sp_copy;
+ }
+
+ /* go get schema */
+ result->objectclasses = vattr_map_entry_build_schema(type_name);
+
+ return result;
+}
+
+/* On top of the map, we need functions to manipulate the SP handles from within */
+
+/* Function to get the SP list from the map, given a type name */
+/* The resulting list is in the map, but is safe to read regardless of concurrent updates */
+vattr_sp_handle_list *vattr_map_sp_getlist(char *type_to_find)
+{
+ int ret = 0;
+ vattr_map_entry *result = NULL;
+ ret = vattr_map_lookup(type_to_find,&result);
+ if (0 == ret) {
+ return (vattr_sp_handle_list*) result->sp_list;
+ } else {
+ return NULL;
+ }
+}
+
+/* same as above, but filters the list based on the supplied backend dn
+ * when we stored these dn based attributes, we concatenated them with
+ * the dn like this dn::attribute, so we need to do two checks for the
+ * attribute, one with, and one without the dn
+ */
+vattr_sp_handle_list *vattr_map_namespace_sp_getlist(Slapi_DN *dn, const char *type_to_find)
+{
+ int ret = 0;
+ vattr_map_entry *result = NULL;
+ vattr_sp_handle_list* return_list = 0;
+
+ ret = vattr_map_lookup(type_to_find,&result);
+ if (0 == ret) {
+ return_list = (vattr_sp_handle_list*) result->sp_list;
+ } else {
+ /* we have allowed the global namespace provider a shot
+ * now it is time to query for split namespace providers
+ */
+ if(dn) {
+ char *split_dn = (char*)slapi_sdn_get_dn(dn);
+ char *split_type_to_find =
+ (char*)PR_smprintf("%s::%s",split_dn, type_to_find);
+
+ if(split_type_to_find)
+ {
+ ret = vattr_map_lookup(split_type_to_find,&result);
+ if (0 == ret) {
+ return_list = (vattr_sp_handle_list*) result->sp_list;
+ }
+
+ PR_smprintf_free(split_type_to_find);
+ }
+ }
+ }
+
+ return return_list;
+}
+
+
+/* Iterator function for the list */
+vattr_sp_handle *vattr_map_sp_next(vattr_sp_handle_list *list, void **hint)
+{
+ vattr_sp_handle *result = NULL;
+ vattr_sp_handle *current = (vattr_sp_handle*) list;
+ PR_ASSERT(list);
+ PR_ASSERT(hint);
+ result = current->next;
+ *hint = current->hint;
+ return result;
+}
+
+/* Iterator function for the list */
+vattr_sp_handle *vattr_map_sp_first(vattr_sp_handle_list *list, void **hint)
+{
+ vattr_sp_handle *result = NULL;
+ PR_ASSERT(list);
+ PR_ASSERT(hint);
+ result = (vattr_sp_handle*)list;
+ *hint = result->hint;
+ return result;
+}
+
+/* Iterator function for the list */
+vattr_sp_handle *vattr_list_sp_next(vattr_sp_handle_list *list)
+{
+ vattr_sp_handle *result = NULL;
+ vattr_sp_handle *current = (vattr_sp_handle*) list;
+ PR_ASSERT(list);
+ result = current->next;
+ return result;
+}
+
+/* Iterator function for the list */
+vattr_sp_handle *vattr_list_sp_first(vattr_sp_handle_list *list)
+{
+ vattr_sp_handle *result = NULL;
+ PR_ASSERT(list);
+ result = (vattr_sp_handle*)list;
+ return result;
+}
+
+/* Function to insert an SP into the map entry for a given type name */
+/* Note that SP's can't ever remmove themselves from the map---if they could
+we'd need to hold a lock on the read path, which we don't want to do.
+So any SP which relinquishes its need to handle a type needs to continue
+to handle the calls on it, but return nothing */
+/* DBDB need to sort out memory ownership here, it's not quite right */
+
+int vattr_map_sp_insert(char *type_to_add, vattr_sp_handle *sp, void *hint)
+{
+ int result = 0;
+ vattr_map_entry *map_entry = NULL;
+ /* Is this type already there ? */
+ result = vattr_map_lookup(type_to_add,&map_entry);
+ /* If it is, add this SP to the list, safely even if readers are traversing the list at the same time */
+ if (0 == result) {
+ int found = 0;
+ vattr_sp_handle *list_entry = NULL;
+ /* Walk the list checking that the daft SP isn't already here */
+ for (list_entry = map_entry->sp_list ; list_entry; list_entry = list_entry->next) {
+ if (list_entry == sp) {
+ found = 1;
+ break;
+ }
+ }
+ /* If it is, we do nothing */
+ if(found) {
+ return 0;
+ }
+ /* We insert the SP handle into the linked list at the head */
+ sp->next = map_entry->sp_list;
+ map_entry->sp_list = sp;
+ } else {
+ /* If not, add it */
+ map_entry = vattr_map_entry_new(type_to_add,sp,hint);
+ if (NULL == map_entry) {
+ return ENOMEM;
+ }
+ return vattr_map_insert(map_entry);
+ }
+ return 0;
+}
+
+/*
+ * returns non-zero if type is a cachable virtual attr, zero otherwise.
+ *
+ * Decision point for caching vattrs...to be expanded to be configurable,
+ * allow sp's to have a say etc.
+*/
+
+static int cache_all = 0;
+
+int slapi_vattrcache_iscacheable( const char * type ) {
+
+ int rc = 0;
+
+ if(/*cache_all ||*/ !slapi_UTF8CASECMP((char *)type, "nsrole")) {
+ rc = 1;
+ }
+
+ return(rc);
+}
+
+/*
+ * slapi_vattrcache_cache_all and slapi_vattrcache_cache_none
+ * ----------------------------------------------------------
+ * limited control for deciding whether to
+ * cache anything, in reality controls whether
+ * to cache cos attributes right now
+ */
+void slapi_vattrcache_cache_all()
+{
+ cache_all = -1;
+}
+
+void slapi_vattrcache_cache_none()
+{
+ cache_all = 0;
+}
+
+void vattrcache_entry_READ_LOCK(const Slapi_Entry *e){
+ PR_RWLock_Rlock(e->e_virtual_lock);
+}
+
+void vattrcache_entry_READ_UNLOCK(const Slapi_Entry *e) {
+ PR_RWLock_Unlock(e->e_virtual_lock);
+
+}
+void vattrcache_entry_WRITE_LOCK(const Slapi_Entry *e){
+ PR_RWLock_Wlock(e->e_virtual_lock);
+
+}
+void vattrcache_entry_WRITE_UNLOCK(const Slapi_Entry *e){
+ PR_RWLock_Unlock(e->e_virtual_lock);
+}
+
+#ifdef VATTR_TEST_CODE
+
+/* Prototype SP begins here */
+
+/* Get value function */
+int vattr_basic_sp_get_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *buffer_flags, void *hint)
+{
+ Slapi_Value *value = NULL;
+ *buffer_flags = 0;
+ *actual_type_name = slapi_ch_strdup("dbtestattr");
+ value = slapi_value_new_string("Hello Client");
+ *results = slapi_ch_calloc(1,sizeof(Slapi_ValueSet));
+ slapi_valueset_init(*results);
+ slapi_valueset_add_value_ext(*results,value,SLAPI_VALUE_FLAG_PASSIN);
+ return 0;
+}
+
+/* Compare value function */
+int vattr_basic_sp_compare_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_Value *test_this, int* result,int flags, void *hint)
+{
+ *result = 0;
+ return 0;
+}
+
+int vattr_basic_sp_list_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags)
+{
+ static char* test_type_name = "dbtestattr";
+ vattr_type_thang thang = {0};
+
+ thang.type_name = test_type_name;
+ thang.type_flags = 0;
+
+ slapi_vattrspi_add_type(type_context,&thang,SLAPI_VIRTUALATTRS_REQUEST_POINTERS);
+ return 0;
+}
+
+int vattr_basic_sp_init()
+{
+ int ret = 0;
+ vattr_sp_handle *my_handle = NULL;
+ /* Register SP */
+ ret = slapi_vattrspi_register(&my_handle,vattr_basic_sp_get_value, vattr_basic_sp_compare_value, vattr_basic_sp_list_types);
+ if (ret) {
+ slapd_nasty(sourcefile,4,0);
+ return ret;
+ }
+ /* Register interest in some attribute over the entire tree */
+ ret = slapi_vattrspi_regattr(my_handle,"dbtestattr","", NULL);
+ if (ret) {
+ slapd_nasty(sourcefile,5,0);
+ return ret;
+ }
+ /* Register interest in some attribute over the entire tree */
+ ret = slapi_vattrspi_regattr(my_handle,"dbtestattr1","", NULL);
+ if (ret) {
+ slapd_nasty(sourcefile,5,0);
+ return ret;
+ }
+ /* Register interest in some attribute over the entire tree */
+ ret = slapi_vattrspi_regattr(my_handle,"dbtestattr2","", NULL);
+ if (ret) {
+ slapd_nasty(sourcefile,5,0);
+ return ret;
+ }
+ /* Register interest in some attribute over the entire tree */
+ ret = slapi_vattrspi_regattr(my_handle,"dbtestatt3r","", NULL);
+ if (ret) {
+ slapd_nasty(sourcefile,5,0);
+ return ret;
+ }
+ return ret;
+}
+
+/* What do we do on shutdown ? */
+int vattr_basic_sp_cleanup()
+{
+ return 0;
+}
+
+#endif
+
+
+
+