/* config block */
typedef struct translucent_info {
BackendDB db; /* captive backend */
+ AttributeName *local; /* valid attrs for local filters */
+ AttributeName *remote; /* valid attrs for remote filters */
int strict;
int no_glue;
int defer_db_open;
static ConfigLDAPadd translucent_ldadd;
static ConfigCfAdd translucent_cfadd;
+static ConfigDriver translucent_cf_gen;
+
+enum {
+ TRANS_LOCAL = 1,
+ TRANS_REMOTE
+};
+
static ConfigTable translucentcfg[] = {
{ "translucent_strict", "on|off", 1, 2, 0,
ARG_ON_OFF|ARG_OFFSET,
"( OLcfgOvAt:14.2 NAME 'olcTranslucentNoGlue' "
"DESC 'Disable automatic glue records for ADD and MODRDN' "
"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "translucent_local", "attr[,attr...]", 1, 2, 0,
+ ARG_STRING|ARG_MAGIC|TRANS_LOCAL,
+ translucent_cf_gen,
+ "( OLcfgOvAt:14.3 NAME 'olcTranslucentLocal' "
+ "DESC 'Attributes to use in local search filter' "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "translucent_remote", "attr[,attr...]", 1, 2, 0,
+ ARG_STRING|ARG_MAGIC|TRANS_REMOTE,
+ translucent_cf_gen,
+ "( OLcfgOvAt:14.4 NAME 'olcTranslucentRemote' "
+ "DESC 'Attributes to use in remote search filter' "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};
return 0;
}
+static int
+translucent_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ translucent_info *ov = on->on_bi.bi_private;
+ AttributeName **an, *a2;
+ int i;
+
+ if ( c->type == TRANS_LOCAL )
+ an = &ov->local;
+ else
+ an = &ov->remote;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ for ( i = 0; !BER_BVISNULL(&(*an)[i].an_name); i++ ) {
+ value_add_one( &c->rvalue_vals, &(*an)[i].an_name );
+ }
+ return ( i < 1 );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->valx < 0 ) {
+ anlist_free( *an, 1, NULL );
+ *an = NULL;
+ } else {
+ i = c->valx;
+ ch_free( (*an)[i].an_name.bv_val );
+ do {
+ (*an)[i] = (*an)[i+1];
+ } while ( !BER_BVISNULL( &(*an)[i].an_name ));
+ }
+ return 0;
+ }
+ a2 = str2anlist( *an, c->argv[1], "," );
+ if ( !a2 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse attribute %s",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg, 0 );
+ return ARG_BAD_CONF;
+ }
+ *an = a2;
+ return 0;
+}
+
static slap_overinst translucent;
/*
/*
** translucent_search_cb()
-** merge local data with the search result
+** merge local data with remote data
**
+** Four cases:
+** 1: remote search, no local filter
+** merge data and send immediately
+** 2: remote search, with local filter
+** merge data and save
+** 3: local search, no remote filter
+** merge data and send immediately
+** 4: local search, with remote filter
+** check list, merge, send, delete
*/
+#define RMT_SIDE 0
+#define LCL_SIDE 1
+#define USE_LIST 2
+
+typedef struct trans_ctx {
+ BackendDB *db;
+ slap_overinst *on;
+ Filter *orig;
+ Filter *rmt;
+ Filter *lcl;
+ Avlnode *list;
+ int step;
+} trans_ctx;
+
static int translucent_search_cb(Operation *op, SlapReply *rs) {
+ trans_ctx *tc;
BackendDB *db;
slap_overinst *on;
- Entry *e, *re = NULL;
+ translucent_info *ov;
+ Entry *le, *re;
Attribute *a, *ax, *an, *as = NULL;
int rc;
+ tc = op->o_callback->sc_private;
+
+ /* Don't let the op complete while we're gathering data */
+ if ( rs->sr_type == REP_RESULT && ( tc->step & USE_LIST ))
+ return 0;
+
if(!op || !rs || rs->sr_type != REP_SEARCH || !rs->sr_entry)
return(SLAP_CB_CONTINUE);
Debug(LDAP_DEBUG_TRACE, "==> translucent_search_cb: %s\n",
rs->sr_entry->e_name.bv_val, 0, 0);
- db = op->o_bd;
- op->o_bd = op->o_callback->sc_private;
- on = (slap_overinst *) op->o_bd->bd_info;
+ on = tc->on;
+ ov = on->on_bi.bi_private;
- rc = overlay_entry_get_ov(op, &rs->sr_entry->e_nname, NULL, NULL, 0, &e, on);
+ db = op->o_bd;
+ re = NULL;
+
+ /* If we have local, get remote */
+ if ( tc->step & LCL_SIDE ) {
+ le = rs->sr_entry;
+ /* If entry is already on list, use it */
+ if ( tc->step & USE_LIST ) {
+ re = tavl_delete( &tc->list, le, entry_dn_cmp );
+ if ( re ) {
+ if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
+ be_entry_release_r( op, rs->sr_entry );
+ }
+ if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
+ entry_free( rs->sr_entry );
+ }
+ rc = test_filter( op, re, tc->orig );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
+ rs->sr_entry = re;
+ return SLAP_CB_CONTINUE;
+ } else {
+ entry_free( re );
+ rs->sr_entry = NULL;
+ return 0;
+ }
+ }
+ }
+ op->o_bd = &ov->db;
+ rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &re );
+ if ( rc == LDAP_SUCCESS && re ) {
+ Entry *tmp = entry_dup( re );
+ be_entry_release_r( op, re );
+ re = tmp;
+ }
+ } else {
+ /* Else we have remote, get local */
+ op->o_bd = tc->db;
+ rc = overlay_entry_get_ov(op, &rs->sr_entry->e_nname, NULL, NULL, 0, &le, on);
+ if ( rc == LDAP_SUCCESS && le ) {
+ re = entry_dup( rs->sr_entry );
+ if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
+ be_entry_release_r( op, rs->sr_entry );
+ }
+ if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
+ entry_free( rs->sr_entry );
+ }
+ } else {
+ le = NULL;
+ }
+ }
/*
-** if we got an entry from local backend:
-** make a copy of this search result;
+** if we got remote and local entry:
** foreach local attr:
-** foreach search result attr:
-** if match, result attr with local attr;
+** foreach remote attr:
+** if match, remote attr with local attr;
** if new local, add to list;
-** append new local attrs to search result;
+** append new local attrs to remote;
**
*/
- if(e && rc == LDAP_SUCCESS) {
- re = entry_dup(rs->sr_entry);
- for(ax = e->e_attrs; ax; ax = ax->a_next) {
-#if 0
- if(is_at_operational(ax->a_desc->ad_type)) continue;
-#endif
+ if ( re && le ) {
+ for(ax = le->e_attrs; ax; ax = ax->a_next) {
for(a = re->e_attrs; a; a = a->a_next) {
if(a->a_desc == ax->a_desc) {
if(a->a_vals != a->a_nvals)
an->a_next = as;
as = an;
}
- overlay_entry_release_ov(op, e, 0, on);
+ /* Dispose of local entry */
+ if ( tc->step & LCL_SIDE ) {
+ if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
+ be_entry_release_r( op, rs->sr_entry );
+ }
+ if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
+ entry_free( rs->sr_entry );
+ }
+ } else {
+ overlay_entry_release_ov(op, le, 0, on);
+ }
/* literally append, so locals are always last */
if(as) {
re->e_attrs = as;
}
}
- rs->sr_entry = re;
- rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
+ if ( tc->step == (USE_LIST|RMT_SIDE) ) {
+ rc = test_filter( op, re, tc->orig );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ tavl_insert( &tc->list, re, entry_dn_cmp, avl_dup_error );
+ } else {
+ entry_free( re );
+ }
+ rs->sr_entry = NULL;
+ rc = 0;
+ } else {
+ rs->sr_entry = re;
+ rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
+ rc = SLAP_CB_CONTINUE;
+ }
+ } else if ( le ) {
+ /* Only a local entry: remote was deleted
+ * Ought to delete the local too...
+ */
+ rc = 0;
+ } else if ( tc->step & USE_LIST ) {
+ /* Only a remote entry, but both filters:
+ * Test the complete filter
+ */
+ rc = test_filter( op, rs->sr_entry, tc->orig );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rc = SLAP_CB_CONTINUE;
+ } else {
+ rc = 0;
+ }
+ } else {
+ /* Only a remote entry, only remote filter:
+ * just pass thru
+ */
+ rc = SLAP_CB_CONTINUE;
}
op->o_bd = db;
- return(SLAP_CB_CONTINUE);
+ return rc;
+}
+
+/* Dup the filter, excluding invalid elements */
+static Filter *
+trans_filter_dup(Operation *op, Filter *f, AttributeName *an)
+{
+ Filter *n = NULL;
+
+ if ( !f )
+ return NULL;
+
+ switch( f->f_choice & SLAPD_FILTER_MASK ) {
+ case SLAPD_FILTER_COMPUTED:
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_result = f->f_result;
+ n->f_next = NULL;
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ if ( ad_inlist( f->f_desc, an )) {
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_desc = f->f_desc;
+ n->f_next = NULL;
+ }
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_SUBSTRINGS:
+ case LDAP_FILTER_EXT:
+ if ( !f->f_av_desc || ad_inlist( f->f_av_desc, an )) {
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_ava = f->f_ava;
+ n->f_next = NULL;
+ }
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT: {
+ Filter **p;
+
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_next = NULL;
+
+ for ( p = &n->f_list, f = f->f_list; f; f = f->f_next ) {
+ *p = trans_filter_dup( op, f, an );
+ if ( !*p )
+ continue;
+ p = &(*p)->f_next;
+ }
+ /* nothing valid in this list */
+ if ( !n->f_list ) {
+ op->o_tmpfree( n, op->o_tmpmemctx );
+ return NULL;
+ }
+ /* Only 1 element in this list */
+ if ((f->f_choice & SLAPD_FILTER_MASK) != LDAP_FILTER_NOT &&
+ !n->f_list->f_next ) {
+ f = n->f_list;
+ *n = *f;
+ op->o_tmpfree( f, op->o_tmpmemctx );
+ }
+ break;
+ }
+ }
+ return n;
}
/*
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
translucent_info *ov = on->on_bi.bi_private;
slap_callback cb = { NULL, NULL, NULL, NULL };
- int rc;
+ trans_ctx tc;
+ Filter *fl, *fr;
+ int rc = 0;
Debug(LDAP_DEBUG_TRACE, "==> translucent_search: <%s> %s\n",
op->o_req_dn.bv_val, op->ors_filterstr.bv_val, 0);
"remote DB not available");
return(rs->sr_err);
}
+
+ fr = ov->remote ? trans_filter_dup( op, op->ors_filter, ov->remote ) : NULL;
+ fl = ov->local ? trans_filter_dup( op, op->ors_filter, ov->local ) : NULL;
cb.sc_response = (slap_response *) translucent_search_cb;
- cb.sc_private = op->o_bd;
+ cb.sc_private = &tc;
cb.sc_next = op->o_callback;
+ tc.db = op->o_bd;
+ tc.on = on;
+ tc.orig = op->ors_filter;
+ tc.rmt = fr;
+ tc.lcl = fl;
+ tc.list = NULL;
+ tc.step = 0;
+
op->o_callback = &cb;
- op->o_bd = &ov->db;
- rc = ov->db.bd_info->bi_op_search(op, rs);
- op->o_bd = cb.sc_private;
+
+ if ( fr || !fl ) {
+ tc.step |= RMT_SIDE;
+ if ( fl ) tc.step |= USE_LIST;
+ op->o_bd = &ov->db;
+ if ( fl )
+ op->ors_filter = fr;
+ rc = ov->db.bd_info->bi_op_search(op, rs);
+ op->o_bd = tc.db;
+ }
+ if ( fl && !rc ) {
+ tc.step |= LCL_SIDE;
+ op->ors_filter = fl;
+ rc = overlay_op_walk( op, rs, op_search, on->on_info, on->on_next );
+ }
+ op->ors_filter = tc.orig;
+ op->o_callback = cb.sc_next;
+ /* Send out anything remaining on the list and finish */
+ if ( tc.step & USE_LIST ) {
+ }
return rc;
}
/*
** translucent_db_close()
-** if the captive backend has a close() method, call it;
-** free any config data;
+** if the captive backend has a close() method, call it
**
*/
/*
** translucent_db_destroy()
-** if the captive backend has a db_destroy() method, call it
+** if the captive backend has a db_destroy() method, call it;
+** free any config data
**
*/
Debug(LDAP_DEBUG_TRACE, "==> translucent_db_destroy\n", 0, 0, 0);
if ( ov ) {
+ if ( ov->remote )
+ anlist_free( ov->remote, 1, NULL );
+ if ( ov->local )
+ anlist_free( ov->local, 1, NULL );
if ( ov->db.be_private != NULL ) {
backend_stopdown_one( &ov->db );
}