+static int
+rwm_attrs( Operation *op, SlapReply *rs, Attribute** a_first, int stripEntryDN )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ dncookie dc;
+ int rc;
+ Attribute **ap;
+ int isupdate;
+
+ /*
+ * Rewrite the dn attrs, if needed
+ */
+ dc.rwmap = rwmap;
+#ifdef ENABLE_REWRITE
+ dc.conn = op->o_conn;
+ dc.rs = NULL;
+#else /* ! ENABLE_REWRITE */
+ dc.tofrom = 0;
+ dc.normalized = 0;
+#endif /* ! ENABLE_REWRITE */
+
+ /* FIXME: the entries are in the remote mapping form;
+ * so we need to select those attributes we are willing
+ * to return, and remap them accordingly */
+
+ /* FIXME: in principle, one could map an attribute
+ * on top of another, which already exists.
+ * As such, in the end there might exist more than
+ * one instance of an attribute.
+ * We should at least check if this occurs, and issue
+ * an error (because multiple instances of attrs in
+ * response are not valid), or merge the values (what
+ * about duplicate values?) */
+ isupdate = be_shadow_update( op );
+ for ( ap = a_first; *ap; ) {
+ struct ldapmapping *mapping = NULL;
+ int drop_missing;
+ int last;
+ Attribute *a;
+
+ if ( SLAP_OPATTRS( rs->sr_attr_flags ) && is_at_operational( (*ap)->a_desc->ad_type ) )
+ {
+ /* go on */ ;
+
+ } else {
+ if ( op->ors_attrs != NULL &&
+ !SLAP_USERATTRS( rs->sr_attr_flags ) &&
+ !ad_inlist( (*ap)->a_desc, op->ors_attrs ) )
+ {
+ goto cleanup_attr;
+ }
+
+ drop_missing = rwm_mapping( &rwmap->rwm_at,
+ &(*ap)->a_desc->ad_cname, &mapping, RWM_REMAP );
+ if ( drop_missing || ( mapping != NULL && BER_BVISEMPTY( &mapping->m_dst ) ) )
+ {
+ goto cleanup_attr;
+ }
+
+ if ( mapping != NULL ) {
+ (*ap)->a_desc = mapping->m_dst_ad;
+ }
+ }
+
+ if ( (*ap)->a_desc == slap_schema.si_ad_entryDN ) {
+ if ( stripEntryDN ) {
+ /* will be generated by frontend */
+ goto cleanup_attr;
+ }
+
+ } else if ( !isupdate
+ && (*ap)->a_desc->ad_type->sat_no_user_mod
+ && (*ap)->a_desc->ad_type != slap_schema.si_at_undefined )
+ {
+ goto next_attr;
+ }
+
+ for ( last = 0; !BER_BVISNULL( &(*ap)->a_vals[last] ); last++ )
+ /* just count */ ;
+
+ if ( last == 0 ) {
+ /* empty? leave it in place because of attrsonly and vlv */
+ goto next_attr;
+ }
+ last--;
+
+ if ( (*ap)->a_desc == slap_schema.si_ad_objectClass
+ || (*ap)->a_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ struct berval *bv;
+
+ for ( bv = (*ap)->a_vals; !BER_BVISNULL( bv ); bv++ ) {
+ struct berval mapped;
+
+ rwm_map( &rwmap->rwm_oc, &bv[0], &mapped, RWM_REMAP );
+ if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
+ ch_free( bv[0].bv_val );
+ BER_BVZERO( &bv[0] );
+ if ( &(*ap)->a_vals[last] > &bv[0] ) {
+ bv[0] = (*ap)->a_vals[last];
+ BER_BVZERO( &(*ap)->a_vals[last] );
+ }
+ last--;
+ bv--;
+
+ } else if ( mapped.bv_val != bv[0].bv_val ) {
+ /*
+ * FIXME: after LBER_FREEing
+ * the value is replaced by
+ * ch_alloc'ed memory
+ */
+ ch_free( bv[0].bv_val );
+ ber_dupbv( &bv[0], &mapped );
+ }
+ }
+
+ /*
+ * It is necessary to try to rewrite attributes with
+ * dn syntax because they might be used in ACLs as
+ * members of groups; since ACLs are applied to the
+ * rewritten stuff, no dn-based subject clause could
+ * be used at the ldap backend side (see
+ * http://www.OpenLDAP.org/faq/data/cache/452.html)
+ * The problem can be overcome by moving the dn-based
+ * ACLs to the target directory server, and letting
+ * everything pass thru the ldap backend. */
+ /* FIXME: handle distinguishedName-like syntaxes, like
+ * nameAndOptionalUID */
+ } else if ( (*ap)->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_src_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+#ifdef ENABLE_REWRITE
+ dc.ctx = "searchAttrDN";
+#endif /* ENABLE_REWRITE */
+ rc = rwm_dnattr_result_rewrite( &dc, (*ap)->a_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_attr;
+ }
+
+ } else if ( (*ap)->a_desc == slap_schema.si_ad_ref ) {
+#ifdef ENABLE_REWRITE
+ dc.ctx = "searchAttrDN";
+#endif /* ENABLE_REWRITE */
+ rc = rwm_referral_result_rewrite( &dc, (*ap)->a_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_attr;
+ }
+ }
+
+ if ( mapping != NULL ) {
+ /* rewrite the attribute description */
+ assert( mapping->m_dst_ad );
+ (*ap)->a_desc = mapping->m_dst_ad;
+ }
+
+next_attr:;
+ ap = &(*ap)->a_next;
+ continue;
+
+cleanup_attr:;
+ a = *ap;
+ *ap = (*ap)->a_next;
+
+ attr_free( a );
+ }
+
+ return 0;
+}
+