From b2093e3016027d6b5cf06b3f91f30769bfc099e2 Mon Sep 17 00:00:00 2001 From: cvsadm Date: Fri, 21 Jan 2005 00:44:34 +0000 Subject: Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth) --- ldap/servers/slapd/entrywsi.c | 1151 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1151 insertions(+) create mode 100644 ldap/servers/slapd/entrywsi.c (limited to 'ldap/servers/slapd/entrywsi.c') diff --git a/ldap/servers/slapd/entrywsi.c b/ldap/servers/slapd/entrywsi.c new file mode 100644 index 00000000..8a39b941 --- /dev/null +++ b/ldap/servers/slapd/entrywsi.c @@ -0,0 +1,1151 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* entrywsi.c - routines for dealing with entries... With State Information */ + +#include "slap.h" +#include "slapi-plugin.h" + +static void resolve_attribute_state(Slapi_Entry *e, Slapi_Attr *a, int attribute_state); + +static int +entry_present_value_to_deleted_value(Slapi_Attr *a, Slapi_Value *v) +{ + Slapi_Value *r= valueset_remove_value(a, &a->a_present_values, v); + if(r!=NULL) + { + slapi_valueset_add_value_ext(&a->a_deleted_values, r, SLAPI_VALUE_FLAG_PASSIN); + } + return LDAP_SUCCESS; +} + +static int +entry_present_value_to_zapped_value(Slapi_Attr *a, Slapi_Value *v) +{ + if(v!=NULL) + { + Slapi_Value *r= valueset_remove_value(a, &a->a_present_values, v); + if(r!=NULL) + { + slapi_value_free(&r); + } + } + return LDAP_SUCCESS; +} + +static int +entry_deleted_value_to_present_value(Slapi_Attr *a, Slapi_Value *v) +{ + Slapi_Value *r= valueset_remove_value(a, &a->a_deleted_values, v); + if(r!=NULL) + { + slapi_valueset_add_value_ext(&a->a_present_values, r, SLAPI_VALUE_FLAG_PASSIN); + } + return LDAP_SUCCESS; +} + +static int +entry_deleted_value_to_zapped_value(Slapi_Attr *a, Slapi_Value *v) +{ + if(v!=NULL) + { + Slapi_Value *r= valueset_remove_value(a, &a->a_deleted_values, v); + if(r!=NULL) + { + slapi_value_free(&r); + } + } + return LDAP_SUCCESS; +} + +static int +entry_present_attribute_to_deleted_attribute(Slapi_Entry *e, Slapi_Attr *a) +{ + attrlist_remove(&e->e_attrs,a->a_type); + attrlist_add(&e->e_deleted_attrs,a); + return LDAP_SUCCESS; +} + +static int +entry_deleted_attribute_to_present_attribute(Slapi_Entry *e, Slapi_Attr *a) +{ + attrlist_remove(&e->e_deleted_attrs,a->a_type); + attrlist_add(&e->e_attrs,a); + return LDAP_SUCCESS; +} + +/* + * Get the first deleted attribute. + * + * Return 0: Return the type and the CSN of the deleted attribute. + * Return -1: There are no deleted attributes. + */ +int +entry_first_deleted_attribute( const Slapi_Entry *e, Slapi_Attr **a) +{ + *a= e->e_deleted_attrs; + return( *a ? 0 : -1 ); +} + +/* + * Get the next deleted attribute. + * + * Return 0: the type and the CSN of the deleted attribute. + * Return -1: no deleted attributes. + */ +int +entry_next_deleted_attribute( const Slapi_Entry *e, Slapi_Attr **a) +{ + *a= (*a)->a_next; + return( *a ? 0 : -1 ); +} + +const CSN * +entry_get_maxcsn ( const Slapi_Entry *entry ) +{ + return entry->e_maxcsn; +} + +void +entry_set_maxcsn ( Slapi_Entry *entry, const CSN *csn ) +{ + if ( NULL == entry->e_maxcsn ) + { + entry->e_maxcsn = csn_dup ( csn ); + } + else if ( csn_compare ( entry->e_maxcsn, csn ) < 0 ) + { + csn_init_by_csn ( entry->e_maxcsn, csn ); + } +} + +/* + * Get the DN CSN of an entry. + */ +const CSN * +entry_get_dncsn(const Slapi_Entry *entry) +{ + return csnset_get_last_csn(entry->e_dncsnset); +} + +/* + * Get the DN CSN set of an entry. + */ +const CSNSet * +entry_get_dncsnset(const Slapi_Entry *entry) +{ + return entry->e_dncsnset; +} + +/* + * Add a DN CSN to an entry. + */ +int +entry_add_dncsn(Slapi_Entry *entry, const CSN *csn) +{ + PR_ASSERT(entry!=NULL); + if(!csnset_contains(entry->e_dncsnset,csn)) + { + csnset_add_csn(&entry->e_dncsnset, CSN_TYPE_VALUE_DISTINGUISHED, csn); + } + return 0; +} + +/* + * Add a DN CSN to an entry, but uses flags to control the behavior + * Using the ENTRY_DNCSN_INCREASING flag makes sure the csnset is in + * order of increasing csn. csnset_insert_csn may not be very fast, so + * we may have to revisit this if it becomes a performance problem. + * In most cases, storing the csn unsorted is ok since the server + * usually makes sure the csn is already in order. However, when doing + * a str2entry, the order is not preserved unless we sort it. + */ +int +entry_add_dncsn_ext(Slapi_Entry *entry, const CSN *csn, PRUint32 flags) +{ + PR_ASSERT(entry!=NULL); + if(!csnset_contains(entry->e_dncsnset,csn)) + { + if (flags & ENTRY_DNCSN_INCREASING) + { + csnset_insert_csn(&entry->e_dncsnset, CSN_TYPE_VALUE_DISTINGUISHED, csn); + } + else + { + csnset_add_csn(&entry->e_dncsnset, CSN_TYPE_VALUE_DISTINGUISHED, csn); + } + } + return 0; +} + +/* + * Set the CSN for all the present values on the entry. + * This is only intended to be used for new entries + * being added. + */ +int +entry_set_csn(Slapi_Entry *entry, const CSN *csn) +{ + Slapi_Attr *a; + + PR_ASSERT(entry!=NULL); + + slapi_entry_first_attr( entry, &a ); + while(a!=NULL) + { + /* + * JCM - it'd be more efficient if the str2entry code + * set a flag on the attribute structure. + */ + if(strcasecmp(a->a_type, SLAPI_ATTR_UNIQUEID)!=0) + { + attr_set_csn(a,csn); + } + slapi_entry_next_attr( entry, a, &a ); + } + return 0; +} + +/* + * Set the Distinguished CSN for the RDN components of the entry. + */ +void +entry_add_rdn_csn(Slapi_Entry *e, const CSN *csn) +{ + char *type; + char *value; + int index; + const Slapi_DN *dn= slapi_entry_get_sdn_const(e); + Slapi_RDN *rdn= slapi_rdn_new_sdn(dn); + index= slapi_rdn_get_first(rdn, &type, &value); + while(index!=-1) + { + Slapi_Attr *a= NULL; + Slapi_Value *v= NULL; + entry_attr_find_wsi(e, type, &a); + if(a!=NULL) + { + struct berval bv; + bv.bv_len= strlen(value); + bv.bv_val= (void*)value; + attr_value_find_wsi(a, &bv, &v); + } + if(v!=NULL) + { + value_update_csn(v,CSN_TYPE_VALUE_DISTINGUISHED,csn); + } + else + { + /* JCM RDN component isn't a present value - this is illegal. */ + } + index= slapi_rdn_get_next(rdn, index, &type, &value); + } + slapi_rdn_free(&rdn); +} + +CSN* +entry_assign_operation_csn ( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *parententry ) +{ + Slapi_Operation *op; + const CSN *basecsn = NULL; + const CSN *parententry_dncsn = NULL; + CSN *opcsn = NULL; + + slapi_pblock_get ( pb, SLAPI_OPERATION, &op ); + + /* + * The replication pre-op would have set op->o_csngen_handler for + * user requests that are against a replica. + */ + if ( op->o_csngen_handler ) + { + /* + * Sync up the CSN generator so that the new csn is greater + * than the entry's maxcsn and/or the parent's max dncsn. + */ + if ( e ) + { + basecsn = entry_get_maxcsn ( e ); + } + if ( parententry ) + { + parententry_dncsn = entry_get_dncsn ( parententry ); + if ( csn_compare ( parententry_dncsn, basecsn ) > 0 ) + { + basecsn = parententry_dncsn; + } + } + opcsn = op->o_csngen_handler ( pb, basecsn ); + + if (NULL != opcsn) + { + operation_set_csn (op, opcsn); + } + } + + return opcsn; +} + +/* + * Purge state information from the entry older than csnUpTo + * + * if csnUpTo is NULL, get rid of all the CSN related info. + * if csnUpTo is non-NULL, purge all info older than csnUpTo + */ +void +entry_purge_state_information(Slapi_Entry *e, const CSN *csnUpTo) +{ + Slapi_Attr *a=NULL; + + PR_ASSERT(e!=NULL); + + for(a = e->e_attrs; NULL != a; a = a->a_next) + { + /* + * 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 + */ + attr_purge_state_information(e, a, csnUpTo); + } + for(a = e->e_deleted_attrs; NULL != a; a = a->a_next) + { + /* + * 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 + */ + attr_purge_state_information(e, a, csnUpTo); + } +} + +/* + * Look for the attribute on the present and deleted attribute lists. + */ +int +entry_attr_find_wsi(Slapi_Entry *e, const char *type, Slapi_Attr **a) +{ + int retVal= ATTRIBUTE_NOTFOUND; + + PR_ASSERT(e!=NULL); + PR_ASSERT(type!=NULL); + PR_ASSERT(a!=NULL); + + /* Look on the present attribute list */ + *a= attrlist_find(e->e_attrs,type); + if(*a!=NULL) + { + /* The attribute is present */ + retVal= ATTRIBUTE_PRESENT; + } + else + { + /* Maybe the attribue was deleted... */ + *a= attrlist_find(e->e_deleted_attrs,type); + if(*a!=NULL) + { + /* The attribute is deleted */ + retVal= ATTRIBUTE_DELETED; + } + else + { + /* The attribute was not found */ + retVal= ATTRIBUTE_NOTFOUND; + } + } + return retVal; +} + +/* + * Add the attribute to the deleted attribute list. + * + * Consumes the attribute. + */ +int +entry_add_deleted_attribute_wsi(Slapi_Entry *e, Slapi_Attr *a) +{ + PR_ASSERT( e!=NULL ); + PR_ASSERT( a!=NULL ); + attrlist_add(&e->e_deleted_attrs, a); + return 0; +} + +/* + * Add the attribute to the present attribute list. + * + * Consumes the attribute. + */ +int +entry_add_present_attribute_wsi(Slapi_Entry *e, Slapi_Attr *a) +{ + PR_ASSERT( e!=NULL ); + PR_ASSERT( a!=NULL ); + attrlist_add(&e->e_attrs, a); + return 0; +} + +/* + * Add a list of values to the attribute, whilst maintaining state information. + * + * Preserves LDAP Information Model constraints, + * returning an LDAP result code. + */ +static int +entry_add_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **bervals, const CSN *csn, int urp, long flags) +{ + int retVal= LDAP_SUCCESS; + Slapi_Value **valuestoadd = NULL; + valuearray_init_bervalarray(bervals,&valuestoadd); /* JCM SLOW FUNCTION */ + if(!valuearray_isempty(valuestoadd)) + { + Slapi_Attr *a= NULL; + long a_flags_orig; + int attr_state= entry_attr_find_wsi(e, type, &a); + if (ATTRIBUTE_NOTFOUND == attr_state) + { + /* Create a new attribute */ + a = slapi_attr_new(); + slapi_attr_init(a, type); + attrlist_add(&e->e_attrs, a); + } + a_flags_orig = a->a_flags; + a->a_flags |= flags; + if(urp) + { + /* + * Consolidate a->a_present_values and the pending values: + * Delete the pending values from a->a_present_values + * and transfer their csnsets to valuestoadd. + */ + valueset_remove_valuearray (&a->a_present_values, a, valuestoadd, + SLAPI_VALUE_FLAG_IGNOREERROR | + SLAPI_VALUE_FLAG_PRESERVECSNSET, NULL); + /* + * Consolidate a->a_deleted_values and the pending values + * similarly. + */ + valueset_remove_valuearray (&a->a_deleted_values, a, valuestoadd, + SLAPI_VALUE_FLAG_IGNOREERROR | + SLAPI_VALUE_FLAG_PRESERVECSNSET, NULL); + + /* Append the pending values to a->a_present_values */ + valuearray_update_csn (valuestoadd,CSN_TYPE_VALUE_UPDATED,csn); + valueset_add_valuearray_ext(&a->a_present_values, valuestoadd, SLAPI_VALUE_FLAG_PASSIN); + slapi_ch_free ( (void **)&valuestoadd ); + + /* + * Now delete non-RDN values from a->a_present_values; and + * restore possible RDN values from a->a_deleted_values + */ + resolve_attribute_state(e, a, attr_state); + retVal= LDAP_SUCCESS; + } + else + { + Slapi_Value **deletedvalues= NULL; + switch(attr_state) + { + case ATTRIBUTE_PRESENT: + /* The attribute is already on the present list */ + break; + case ATTRIBUTE_DELETED: + /* Move the deleted attribute onto the present list */ + entry_deleted_attribute_to_present_attribute(e, a); + break; + case ATTRIBUTE_NOTFOUND: + /* No-op - attribute was initialized & added to entry above */ + break; + } + /* Check if any of the values to be added are on the deleted list */ + valueset_remove_valuearray(&a->a_deleted_values, a, valuestoadd, SLAPI_VALUE_FLAG_IGNOREERROR,&deletedvalues); /* JCM Check return code */ + if(deletedvalues!=NULL && deletedvalues[0]!=NULL) + { + /* Some of the values to be added were on the deleted list */ + Slapi_Value **v= NULL; + Slapi_ValueSet vs; + /* Add each deleted value to the present list */ + valuearray_update_csn(deletedvalues,CSN_TYPE_VALUE_UPDATED,csn); + valueset_add_valuearray_ext(&a->a_present_values, deletedvalues, SLAPI_VALUE_FLAG_PASSIN); + /* Remove the deleted values from the values to add */ + valueset_set_valuearray_passin(&vs,valuestoadd); + valueset_remove_valuearray(&vs, a, deletedvalues, SLAPI_VALUE_FLAG_IGNOREERROR, &v); + valuestoadd= valueset_get_valuearray(&vs); + valuearray_free(&v); + slapi_ch_free((void **)&deletedvalues); + } + valuearray_update_csn(valuestoadd,CSN_TYPE_VALUE_UPDATED,csn); + retVal= attr_add_valuearray(a, valuestoadd, slapi_entry_get_dn_const(e)); + valuearray_free(&valuestoadd); + } + a->a_flags = a_flags_orig; + } + return(retVal); +} + +/* + * Delete a list of values from an attribute, whilst maintaining state information. + * + * Preserves LDAP Information Model constraints, + * returning an LDAP result code. + */ +static int +entry_delete_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp, int mod_op) +{ + int retVal= LDAP_SUCCESS; + Slapi_Attr *a= NULL; + int attr_state= entry_attr_find_wsi(e, type, &a); + if(attr_state==ATTRIBUTE_PRESENT || (attr_state==ATTRIBUTE_DELETED && urp)) + { + /* The attribute is on the present list, or the deleted list and we're doing URP */ + if ( vals == NULL || vals[0] == NULL ) + { + /* delete the entire attribute */ + LDAPDebug( LDAP_DEBUG_ARGS, "removing entire attribute %s\n", type, 0, 0 ); + attr_set_deletion_csn(a,csn); + if(urp) + { + resolve_attribute_state(e, a, attr_state); /* ABSOLVED */ + } + else + { + if(!slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE)) + { + /* We don't maintain a deleted value list for single valued attributes */ + valueset_add_valueset(&a->a_deleted_values, &a->a_present_values); /* JCM Would be better to passin the valuestodelete */ + } + slapi_valueset_done(&a->a_present_values); + entry_present_attribute_to_deleted_attribute(e, a); + } + retVal= LDAP_SUCCESS; /* This Operation always succeeds when the attribute is Present */ + } + else + { + /* delete some specific values */ + Slapi_Value **valuestodelete= NULL; + valuearray_init_bervalarray(vals,&valuestodelete); /* JCM SLOW FUNCTION */ + if(urp) + { + Slapi_Value **valuesupdated= NULL; + valueset_update_csn_for_valuearray(&a->a_present_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated); + /* if we removed the last value, we need to mark the attribute as deleted + the resolve_attribute_state() code will "resurrect" the attribute if + there are present values with a later CSN - otherwise, even though + the value will be updated with a VDCSN which is later than the VUCSN, + the attribute will not be deleted */ + if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE) && valuesupdated && + *valuesupdated) + { + attr_set_deletion_csn(a,csn); + } + valuearray_free(&valuesupdated); + valueset_update_csn_for_valuearray(&a->a_deleted_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated); + valuearray_free(&valuesupdated); + valuearray_update_csn(valuestodelete,CSN_TYPE_VALUE_DELETED,csn); + valueset_add_valuearray_ext(&a->a_deleted_values, valuestodelete, SLAPI_VALUE_FLAG_PASSIN); + /* all the elements in valuestodelete are passed; + * should free valuestodelete only (don't call valuearray_free) + * [622023] */ + slapi_ch_free((void **)&valuestodelete); + resolve_attribute_state(e, a, attr_state); + retVal= LDAP_SUCCESS; + } + else + { + Slapi_Value **deletedvalues= NULL; + retVal= valueset_remove_valuearray(&a->a_present_values, a, valuestodelete, 0 /* Do Not Ignore Errors */,&deletedvalues); + if(retVal==LDAP_SUCCESS && deletedvalues != NULL) + { + if(!slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE)) + { + /* We don't maintain a deleted value list for single valued attributes */ + /* Add each deleted value to the deleted set */ + valuearray_update_csn(deletedvalues,CSN_TYPE_VALUE_DELETED,csn); + valueset_add_valuearray_ext(&a->a_deleted_values, deletedvalues, SLAPI_VALUE_FLAG_PASSIN); + slapi_ch_free((void **)&deletedvalues); + } + else { + valuearray_free(&deletedvalues); + } + if(valueset_isempty(&a->a_present_values)) + { + /* There are no present values, so move the + * attribute to the deleted attribute list. */ + entry_present_attribute_to_deleted_attribute(e, a); + } + } + else if (retVal != LDAP_SUCCESS) + { + /* Failed + * - Duplicate value + * - Value not found + * - Operations error + */ + if ( retVal==LDAP_OPERATIONS_ERROR ) + { + LDAPDebug( LDAP_DEBUG_ANY, "Possible existing duplicate " + "value for attribute type %s found in " + "entry %s\n", a->a_type, slapi_entry_get_dn_const(e), 0 ); + } + } + valuearray_free(&valuestodelete); + } + } + } + else if (attr_state==ATTRIBUTE_DELETED) + { + retVal= LDAP_NO_SUCH_ATTRIBUTE; + } + else if (attr_state==ATTRIBUTE_NOTFOUND) + { + if (!urp) + { + /* Only warn if not urping */ + LDAPDebug( LDAP_DEBUG_ARGS, "could not find attribute %s\n", type, 0, 0 ); + } + retVal= LDAP_NO_SUCH_ATTRIBUTE; + if (LDAP_MOD_REPLACE == mod_op) + { + /* Create a new attribute and set the adcsn */ + Slapi_Attr *a = slapi_attr_new(); + slapi_attr_init(a, type); + /* According to URP there should be an adcsn. + * According to Tests, there should not + * since the attribute never existed + * Tests 26 and 27 of MMRepl state. */ + if (urp) + attr_set_deletion_csn(a,csn); + attrlist_add(&e->e_attrs, a); + } + } + return( retVal ); +} + +/* + * Replace all the values of an attribute with a list of attribute values. + * + * Preserves LDAP Information Model constraints, + * returning an LDAP result code. + */ +static int +entry_replace_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp) +{ + /* + * Remove all existing values. + */ + entry_delete_present_values_wsi(e, type, NULL /* Delete all values */, csn, urp, LDAP_MOD_REPLACE); + + /* + * Add the new values. If there are no new values, + * slapi_entry_add_values() returns LDAP_SUCCESS and so the + * attribute remains deleted (which is the correct outcome). + */ + return( entry_add_present_values_wsi( e, type, vals, csn, urp, SLAPI_ATTR_FLAG_CMP_BITBYBIT )); +} + +/* + * Applies the modification to the entry whilst + * maintaining state information. + */ +int +entry_apply_mod_wsi(Slapi_Entry *e, const LDAPMod *mod, const CSN *csn, int urp) +{ + int retVal= LDAP_SUCCESS; + int i; + + switch ( mod->mod_op & ~LDAP_MOD_BVALUES ) + { + case LDAP_MOD_ADD: + LDAPDebug( LDAP_DEBUG_ARGS, " add: %s\n", mod->mod_type, 0, 0 ); + retVal = entry_add_present_values_wsi( e, mod->mod_type, mod->mod_bvalues, csn, urp, 0 ); + break; + + case LDAP_MOD_DELETE: + LDAPDebug( LDAP_DEBUG_ARGS, " delete: %s\n", mod->mod_type, 0, 0 ); + retVal = entry_delete_present_values_wsi( e, mod->mod_type, mod->mod_bvalues, csn, urp, mod->mod_op ); + break; + + case LDAP_MOD_REPLACE: + LDAPDebug( LDAP_DEBUG_ARGS, " replace: %s\n", mod->mod_type, 0, 0 ); + retVal = entry_replace_present_values_wsi( e, mod->mod_type, mod->mod_bvalues, csn, urp ); + break; + } + for ( i = 0; mod->mod_bvalues != NULL && mod->mod_bvalues[i] != NULL; i++ ) + { + LDAPDebug( LDAP_DEBUG_ARGS, " %s: %s\n", mod->mod_type, mod->mod_bvalues[i]->bv_val, 0 ); + } + LDAPDebug( LDAP_DEBUG_ARGS, " -\n", 0, 0, 0 ); + + return retVal; +} + +/* + * Applies the set of modifications to the entry whilst + * maintaining state information. + */ +int +entry_apply_mods_wsi(Slapi_Entry *e, Slapi_Mods *smods, const CSN *csn, int urp) +{ + int retVal= LDAP_SUCCESS; + LDAPMod *mod; + + LDAPDebug( LDAP_DEBUG_TRACE, "=> entry_apply_mods_wsi\n", 0, 0, 0 ); + + mod = slapi_mods_get_first_mod(smods); + while(NULL!=mod && retVal==LDAP_SUCCESS) + { + if(csn!=NULL) + { + retVal= entry_apply_mod_wsi(e, mod, csn, urp); + } + else + { + retVal= entry_apply_mod(e, mod); + } + mod = slapi_mods_get_next_mod(smods); + } + + LDAPDebug( LDAP_DEBUG_TRACE, "<= entry_apply_mods_wsi %d\n", retVal, 0, 0 ); + + return retVal; +} + +/* + * This code implements a computed attribute called 'nscpEntryWSI'. + * By specifically asking for this attribute the client will recieve + * an LDIF dump of the entry with all its state information. + * + * JCM - Security... Only for the Directory Manager. + */ +static const char *nscpEntryWSI= "nscpEntryWSI"; +/* + */ +static int +entry_compute_nscpentrywsi(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn) +{ + int rc = 0; + + if ( strcasecmp(type, nscpEntryWSI ) == 0) + { + /* If not, we return it as zero */ + char *es; + char *s; + char *p; + int len; + Slapi_Attr our_attr; + slapi_attr_init(&our_attr, nscpEntryWSI); + our_attr.a_flags = SLAPI_ATTR_FLAG_OPATTR; + es= slapi_entry2str_with_options(e, &len, SLAPI_DUMP_STATEINFO | SLAPI_DUMP_UNIQUEID | SLAPI_DUMP_NOWRAP); + s= es; + p= ldif_getline( &s ); + while(p!=NULL) + { + Slapi_Value *v; + char *t, *d; + /* Strip out the Continuation Markers (JCM - I think that NOWRAP means we don't need to do this any more)*/ + for ( t = p, d = p; *t; t++ ) + { + if ( *t != 0x01 ) + *d++ = *t; + } + *d = '\0'; + v= slapi_value_new_string(p); + slapi_attr_add_value(&our_attr,v); + slapi_value_free(&v); + p= ldif_getline( &s ); + } + slapi_ch_free((void**)&es); + rc = (*outputfn) (c, &our_attr, e); + attr_done(&our_attr); + return (rc); + } + + return -1; /* I see no ships */ +} + + +int +entry_computed_attr_init() +{ + slapi_compute_add_evaluator(entry_compute_nscpentrywsi); + return 0; +} + +static void +purge_attribute_state_multi_valued(const Slapi_Attr *a, Slapi_Value *v) +{ + const CSN *vdcsn= value_get_csn(v,CSN_TYPE_VALUE_DELETED); + const CSN *vucsn= value_get_csn(v,CSN_TYPE_VALUE_UPDATED); + if(vdcsn && csn_compare(vdcsn,vucsn)<0) + { + value_remove_csn(v,CSN_TYPE_VALUE_DELETED); + } +} + +/* + * utility function for value_distinguished_at_csn... + */ +static const CSN * +vdac_sniff_value(Slapi_ValueSet *vs, const Slapi_Value *v, const CSN *csn, const CSN *most_recent_mdcsn) +{ + const CSN *mdcsn= value_get_csn(v,CSN_TYPE_VALUE_DISTINGUISHED); + if(mdcsn!=NULL) + { + /* This value was/is distinguished... */ + if(csn_compare(csn,most_recent_mdcsn)<0) + { + /* ...and was distinguished before the point in time we're interested in... */ + int r= csn_compare(mdcsn,most_recent_mdcsn); + if(r>0) + { + /* ...and is the most recent MDCSN we've seen thus far. */ + slapi_valueset_done(vs); + slapi_valueset_add_value(vs,v); + most_recent_mdcsn= mdcsn; + } + else if(r==0) + { + /* ...and is as recent as the last most recent MDCSN we've seen thus far. */ + /* Must have been a multi-valued RDN */ + slapi_valueset_add_value(vs,v); + } + } + } + return most_recent_mdcsn; +} + +/* + * utility function for value_distinguished_at_csn... + */ +static const CSN * +vdac_sniff_attribute(Slapi_ValueSet *vs, Slapi_Attr *a, const CSN *csn, const CSN *most_recent_mdcsn) +{ + Slapi_Value *v; + int i= slapi_attr_first_value( a, &v ); + while(i!=-1) + { + most_recent_mdcsn= vdac_sniff_value( vs, v, csn, most_recent_mdcsn ); + i= slapi_attr_next_value( a, i, &v ); + } + i= attr_first_deleted_value( a, &v ); + while(i!=-1) + { + most_recent_mdcsn= vdac_sniff_value( vs, v, csn, most_recent_mdcsn ); + i= attr_next_deleted_value( a, i, &v ); + } + return most_recent_mdcsn; +} + +/* + * utility function for value_distinguished_at_csn... + * + * Return the set of values that made up the RDN at or before the csn point. + */ +static const CSN * +distinguished_values_at_csn(const Slapi_Entry *e, const CSN *csn, Slapi_ValueSet *vs) +{ + const CSN *most_recent_mdcsn= NULL; + Slapi_Attr *a; + int i= slapi_entry_first_attr( e, &a ); + while(i!=-1) + { + most_recent_mdcsn= vdac_sniff_attribute( vs, a, csn, most_recent_mdcsn); + i= slapi_entry_next_attr( e, a, &a ); + } + i= entry_first_deleted_attribute( e, &a ); + while(i!=-1) + { + most_recent_mdcsn= vdac_sniff_attribute( vs, a, csn, most_recent_mdcsn); + i= entry_next_deleted_attribute( e, &a ); + } + return most_recent_mdcsn; +} + +/* + * Work out if the value was distinguished at time csn. + */ +static int +value_distinguished_at_csn(const Slapi_Entry *e, const Slapi_Attr *original_attr, Slapi_Value *original_value, const CSN *csn) +{ + int r= 0; + const CSN *mdcsn= value_get_csn(original_value,CSN_TYPE_VALUE_DISTINGUISHED); + if(mdcsn!=NULL) + { + /* + * Oh bugger. This means that we have to work out what the RDN components + * were at this point in time. This is non-trivial since we must walk + * through all the present and deleted attributes and their present and + * deleted values. Slow :-( + */ + Slapi_ValueSet *vs= slapi_valueset_new(); + const CSN *most_recent_mdcsn= distinguished_values_at_csn(e, csn, vs); + /* + * We now know what the RDN components were at the point in time we're interested in. + * And the question we need to answer is :- + * 'Was the provided value one of those RDN components?' + */ + if(most_recent_mdcsn!=NULL) + { + Slapi_Value *v; + int i= slapi_valueset_first_value( vs, &v ); + while(i!=-1) + { + if(slapi_value_compare(original_attr, original_value, v)==0) + { + /* This value was distinguished at the time in question. */ + r= 1; + i= -1; + } + else + { + i= slapi_valueset_next_value( vs, i, &v ); + } + } + } + slapi_valueset_free(vs); + } + else + { + /* This value has never been distinguished */ + r= 0; + } + return r; +} + +static void +resolve_attribute_state_multi_valued(Slapi_Entry *e, Slapi_Attr *a, int attribute_state) +{ + int i; + const CSN *adcsn= attr_get_deletion_csn(a); + Slapi_ValueSet *vs= valueset_dup(&a->a_present_values); /* JCM This is slow... but otherwise we end up iterating through a changing array */ + Slapi_Value *v; + + /* Loop over the present attribute values */ + i= slapi_valueset_first_value( vs, &v ); + while(v!=NULL) + { + const CSN *vdcsn; + const CSN *vucsn; + const CSN *deletedcsn; + /* This call ensures that the value does not contain a deletion_csn + * which is before the presence_csn or distinguished_csn of the value. + */ + purge_attribute_state_multi_valued(a, v); + vdcsn= value_get_csn(v, CSN_TYPE_VALUE_DELETED); + vucsn= value_get_csn(v, CSN_TYPE_VALUE_UPDATED); + deletedcsn= csn_max(vdcsn, adcsn); + if(csn_compare(vucsn,deletedcsn)<0) /* check if the attribute or value was deleted after the value was last updated */ + { + if(!value_distinguished_at_csn(e, a, v, deletedcsn)) + { + entry_present_value_to_deleted_value(a,v); + } + } + i= slapi_valueset_next_value( vs, i, &v ); + } + slapi_valueset_free(vs); + + /* Loop over the deleted attribute values */ + vs= valueset_dup(&a->a_deleted_values); /* JCM This is slow... but otherwise we end up iterating through a changing array */ + i= slapi_valueset_first_value( vs, &v ); + while(v!=NULL) + { + const CSN *vdcsn; + const CSN *vucsn; + const CSN *deletedcsn; + /* This call ensures that the value does not contain a deletion_csn which is before the presence_csn or distinguished_csn of the value. */ + purge_attribute_state_multi_valued(a, v); + vdcsn= value_get_csn(v, CSN_TYPE_VALUE_DELETED); + vucsn= value_get_csn(v, CSN_TYPE_VALUE_UPDATED); + deletedcsn= csn_max(vdcsn, adcsn); + if((csn_compare(vucsn,deletedcsn)>=0) || /* check if the attribute or value was deleted after the value was last updated */ + value_distinguished_at_csn(e, a, v, deletedcsn)) + { + entry_deleted_value_to_present_value(a,v); + } + i= slapi_valueset_next_value( vs, i, &v ); + } + slapi_valueset_free(vs); + + if(valueset_isempty(&a->a_present_values)) + { + if(attribute_state==ATTRIBUTE_PRESENT) + { + entry_present_attribute_to_deleted_attribute(e, a); + } + } + else + { + if(attribute_state==ATTRIBUTE_DELETED) + { + entry_deleted_attribute_to_present_attribute(e, a); + } + } +} + +static void +resolve_attribute_state_single_valued(Slapi_Entry *e, Slapi_Attr *a, int attribute_state) +{ + Slapi_Value *current_value= NULL; + Slapi_Value *pending_value= NULL; + Slapi_Value *new_value= NULL; + const CSN *current_value_vucsn; + const CSN *pending_value_vucsn; + const CSN *adcsn; + int i; + + /* + * this call makes sure that the attribute does not have a pending_value + * or deletion_csn which is before the current_value. + */ + i= slapi_attr_first_value(a,¤t_value); + if(i!=-1) + { + slapi_attr_next_value(a,i,&new_value); + } + attr_first_deleted_value(a,&pending_value); + + /* purge_attribute_state_single_valued */ + adcsn= attr_get_deletion_csn(a); + current_value_vucsn= value_get_csn(current_value, CSN_TYPE_VALUE_UPDATED); + pending_value_vucsn= value_get_csn(pending_value, CSN_TYPE_VALUE_UPDATED); + if((pending_value!=NULL && (csn_compare(adcsn, pending_value_vucsn)<0)) || + (pending_value==NULL && (csn_compare(adcsn, current_value_vucsn)<0))) + { + attr_set_deletion_csn(a,NULL); + adcsn= NULL; + } + + if(new_value==NULL) + { + /* check if the pending value should become the current value */ + if(pending_value!=NULL) + { + if(!value_distinguished_at_csn(e,a,current_value,pending_value_vucsn)) + { + /* attribute.current_value = attribute.pending_value; */ + /* attribute.pending_value = NULL; */ + entry_present_value_to_zapped_value(a,current_value); + entry_deleted_value_to_present_value(a,pending_value); + current_value= pending_value; + pending_value= NULL; + current_value_vucsn= pending_value_vucsn; + pending_value_vucsn= NULL; + } + } + /* check if the current value should be deleted */ + if(current_value!=NULL) + { + if(csn_compare(adcsn,current_value_vucsn)>0) /* check if the attribute was deleted after the value was last updated */ + { + if(!value_distinguished_at_csn(e,a,current_value,current_value_vucsn)) + { + entry_present_value_to_zapped_value(a,current_value); + current_value= NULL; + current_value_vucsn= NULL; + } + } + } + } + else /* addition of a new value */ + { + const CSN *new_value_vucsn= value_get_csn(new_value,CSN_TYPE_VALUE_UPDATED); + if(csn_compare(new_value_vucsn,current_value_vucsn)<0) + { + /* + * if the new value was distinguished at the time the current value was added + * then the new value should become current + */ + if(value_distinguished_at_csn(e,a,new_value,current_value_vucsn)) + { + /* attribute.pending_value = attribute.current_value */ + /* attribute.current_value = new_value */ + if(pending_value==NULL) + { + entry_present_value_to_deleted_value(a,current_value); + } + else + { + entry_present_value_to_zapped_value(a,current_value); + } + pending_value= current_value; + current_value= new_value; + new_value= NULL; + pending_value_vucsn= current_value_vucsn; + current_value_vucsn= new_value_vucsn; + } + else + { + /* new_value= NULL */ + entry_present_value_to_zapped_value(a, new_value); + new_value= NULL; + } + } + else /* new value is after the current value */ + { + if(!value_distinguished_at_csn(e, a, current_value, new_value_vucsn)) + { + /* attribute.current_value = new_value */ + entry_present_value_to_zapped_value(a, current_value); + current_value= new_value; + new_value= NULL; + current_value_vucsn= new_value_vucsn; + } + else /* value is distinguished - check if we should replace the current pending value */ + { + if(csn_compare(new_value_vucsn, pending_value_vucsn)>0) + { + /* attribute.pending_value = new_value */ + entry_deleted_value_to_zapped_value(a,pending_value); + entry_present_value_to_deleted_value(a,new_value); + pending_value= new_value; + new_value= NULL; + pending_value_vucsn= new_value_vucsn; + } + } + } + } + + /* + * This call ensures that the attribute does not have a pending_value + * or a deletion_csn that is earlier than the current_value. + */ + /* purge_attribute_state_single_valued */ + if((pending_value!=NULL && (csn_compare(adcsn, pending_value_vucsn)<0)) || + (pending_value==NULL && (csn_compare(adcsn, current_value_vucsn)<0))) + { + attr_set_deletion_csn(a,NULL); + adcsn= NULL; + } + + /* set attribute state */ + if(current_value==NULL) + { + if(attribute_state==ATTRIBUTE_PRESENT) + { + entry_present_attribute_to_deleted_attribute(e, a); + } + } + else + { + if(attribute_state==ATTRIBUTE_DELETED) + { + entry_deleted_attribute_to_present_attribute(e, a); + } + } +} + +static void +resolve_attribute_state(Slapi_Entry *e, Slapi_Attr *a, int attribute_state) +{ + if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE)) + { + resolve_attribute_state_single_valued(e,a,attribute_state); + } + else + { + resolve_attribute_state_multi_valued(e,a,attribute_state); + } +} -- cgit