/* addpartial-overlay.c */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 2004-2022 The OpenLDAP Foundation. * Portions Copyright (C) 2004 Virginia Tech, David Hawes. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in file LICENSE in the * top-level directory of the distribution or, alternatively, at * http://www.OpenLDAP.org/license.html. */ /* ACKNOLEDGEDMENTS: * This work was initially developed by David Hawes of Virginia Tech * for inclusion in OpenLDAP Software. */ /* addpartial-overlay * * This is an OpenLDAP overlay that intercepts ADD requests, determines if a * change has actually taken place for that record, and then performs a modify * request for those values that have changed (modified, added, deleted). If * the record has not changed in any way, it is ignored. If the record does not * exist, the record falls through to the normal add mechanism. This overlay is * useful for replicating from sources that are not LDAPs where it is easier to * build entire records than to determine the changes (i.e. a database). */ #include "portable.h" #include "slap.h" static int collect_error_msg_cb( Operation *op, SlapReply *rs); static slap_overinst addpartial; /** * The meat of the overlay. Search for the record, determine changes, take * action or fall through. */ static int addpartial_add( Operation *op, SlapReply *rs) { Operation nop = *op; Entry *toAdd = NULL; Entry *found = NULL; slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; int rc; toAdd = op->oq_add.rs_e; Debug(LDAP_DEBUG_TRACE, "%s: toAdd->e_nname.bv_val: %s\n", addpartial.on_bi.bi_type, toAdd->e_nname.bv_val ); /* if the user doesn't have access, fall through to the normal ADD */ if(!access_allowed(op, toAdd, slap_schema.si_ad_entry, NULL, ACL_WRITE, NULL)) { return SLAP_CB_CONTINUE; } rc = overlay_entry_get_ov(&nop, &nop.o_req_ndn, NULL, NULL, 0, &found, on); if(rc != LDAP_SUCCESS) { Debug(LDAP_DEBUG_TRACE, "%s: no entry found, falling through to normal add\n", addpartial.on_bi.bi_type ); return SLAP_CB_CONTINUE; } else { Debug(LDAP_DEBUG_TRACE, "%s: found the dn\n", addpartial.on_bi.bi_type ); if(found) { Attribute *attr = NULL; Attribute *at = NULL; int ret; Modifications *mods = NULL; Modifications **modtail = &mods; Modifications *mod = NULL; Debug(LDAP_DEBUG_TRACE, "%s: have an entry!\n", addpartial.on_bi.bi_type ); /* determine if the changes are in the found entry */ for(attr = toAdd->e_attrs; attr; attr = attr->a_next) { if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue; at = attr_find(found->e_attrs, attr->a_desc); if(!at) { Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s not found!\n", addpartial.on_bi.bi_type, attr->a_desc->ad_cname.bv_val ); mod = (Modifications *) ch_malloc(sizeof( Modifications)); mod->sml_flags = 0; mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES; mod->sml_op &= LDAP_MOD_OP; mod->sml_next = NULL; mod->sml_desc = attr->a_desc; mod->sml_type = attr->a_desc->ad_cname; mod->sml_values = attr->a_vals; mod->sml_nvalues = attr->a_nvals; mod->sml_numvals = attr->a_numvals; *modtail = mod; modtail = &mod->sml_next; } else { MatchingRule *mr = attr->a_desc->ad_type->sat_equality; struct berval *bv; const char *text; int acount , bcount; Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s found\n", addpartial.on_bi.bi_type, attr->a_desc->ad_cname.bv_val ); for(bv = attr->a_vals, acount = 0; bv->bv_val != NULL; bv++, acount++) { /* count num values for attr */ } for(bv = at->a_vals, bcount = 0; bv->bv_val != NULL; bv++, bcount++) { /* count num values for attr */ } if(acount != bcount) { Debug(LDAP_DEBUG_TRACE, "%s: acount != bcount, %s\n", addpartial.on_bi.bi_type, "replace all" ); mod = (Modifications *) ch_malloc(sizeof( Modifications)); mod->sml_flags = 0; mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES; mod->sml_op &= LDAP_MOD_OP; mod->sml_next = NULL; mod->sml_desc = attr->a_desc; mod->sml_type = attr->a_desc->ad_cname; mod->sml_values = attr->a_vals; mod->sml_nvalues = attr->a_nvals; mod->sml_numvals = attr->a_numvals; *modtail = mod; modtail = &mod->sml_next; continue; } for(bv = attr->a_vals; bv->bv_val != NULL; bv++) { struct berval *v; ret = -1; for(v = at->a_vals; v->bv_val != NULL; v++) { int r; if(mr && ((r = value_match(&ret, attr->a_desc, mr, SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, bv, v, &text)) == 0)) { if(ret == 0) break; } else { Debug(LDAP_DEBUG_TRACE, "%s: \tvalue DNE, r: %d \n", addpartial.on_bi.bi_type, r ); ret = strcmp(bv->bv_val, v->bv_val); if(ret == 0) break; } } if(ret == 0) { Debug(LDAP_DEBUG_TRACE, "%s: \tvalue %s exists, ret: %d\n", addpartial.on_bi.bi_type, bv->bv_val, ret); } else { Debug(LDAP_DEBUG_TRACE, "%s: \tvalue %s DNE, ret: %d\n", addpartial.on_bi.bi_type, bv->bv_val, ret); mod = (Modifications *) ch_malloc(sizeof( Modifications)); mod->sml_flags = 0; mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES; mod->sml_op &= LDAP_MOD_OP; mod->sml_next = NULL; mod->sml_desc = attr->a_desc; mod->sml_type = attr->a_desc->ad_cname; mod->sml_values = attr->a_vals; mod->sml_nvalues = attr->a_nvals; mod->sml_numvals = attr->a_numvals; *modtail = mod; modtail = &mod->sml_next; break; } } } } /* determine if any attributes were deleted */ for(attr = found->e_attrs; attr; attr = attr->a_next) { if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue; at = NULL; at = attr_find(toAdd->e_attrs, attr->a_desc); if(!at) { Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s not found in new entry!!!\n", addpartial.on_bi.bi_type, attr->a_desc->ad_cname.bv_val ); mod = (Modifications *) ch_malloc(sizeof( Modifications)); mod->sml_flags = 0; mod->sml_op = LDAP_MOD_REPLACE; mod->sml_next = NULL; mod->sml_desc = attr->a_desc; mod->sml_type = attr->a_desc->ad_cname; mod->sml_values = NULL; mod->sml_nvalues = NULL; mod->sml_numvals = 0; *modtail = mod; modtail = &mod->sml_next; } else { Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s found in new entry\n", addpartial.on_bi.bi_type, at->a_desc->ad_cname.bv_val ); } } overlay_entry_release_ov(&nop, found, 0, on); if(mods) { Modifications *m = NULL; Modifications *toDel; int modcount; slap_callback nullcb = { NULL, collect_error_msg_cb, NULL, NULL }; Debug(LDAP_DEBUG_TRACE, "%s: mods to do...\n", addpartial.on_bi.bi_type ); nop.o_tag = LDAP_REQ_MODIFY; nop.orm_modlist = mods; nop.orm_no_opattrs = 0; nop.o_callback = &nullcb; nop.o_bd->bd_info = (BackendInfo *) on->on_info; for(m = mods, modcount = 0; m; m = m->sml_next, modcount++) { /* count number of mods */ } Debug(LDAP_DEBUG_TRACE, "%s: number of mods: %d\n", addpartial.on_bi.bi_type, modcount ); if(nop.o_bd->be_modify) { SlapReply nrs = { REP_RESULT }; rc = (nop.o_bd->be_modify)(&nop, &nrs); } if(rc == LDAP_SUCCESS) { Debug(LDAP_DEBUG_TRACE, "%s: modify successful\n", addpartial.on_bi.bi_type ); } else { Debug(LDAP_DEBUG_TRACE, "%s: modify unsuccessful: %d\n", addpartial.on_bi.bi_type, rc ); rs->sr_err = rc; if(nullcb.sc_private) { rs->sr_text = nullcb.sc_private; } } Debug(LDAP_DEBUG_TRACE, "%s: freeing mods...\n", addpartial.on_bi.bi_type ); for(toDel = mods; toDel; toDel = mods) { mods = mods->sml_next; ch_free(toDel); } } else { Debug(LDAP_DEBUG_TRACE, "%s: no mods to process\n", addpartial.on_bi.bi_type ); } } else { Debug(LDAP_DEBUG_TRACE, "%s: no entry!\n", addpartial.on_bi.bi_type ); } op->o_callback = NULL; send_ldap_result( op, rs ); ch_free((void *)rs->sr_text); rs->sr_text = NULL; return LDAP_SUCCESS; } } static int collect_error_msg_cb( Operation *op, SlapReply *rs) { if(rs->sr_text) { op->o_callback->sc_private = (void *) ch_strdup(rs->sr_text); } return LDAP_SUCCESS; } int addpartial_init() { addpartial.on_bi.bi_type = "addpartial"; addpartial.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; addpartial.on_bi.bi_op_add = addpartial_add; return (overlay_register(&addpartial)); } int init_module(int argc, char *argv[]) { return addpartial_init(); }