1 /* dupent.c - LDAP Control for a Duplicate Entry Representation of Search Results */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2006-2011 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
17 * This work was initially developed by Pierangelo Masarati for inclusion
18 * in OpenLDAP Software.
22 * LDAP Control for a Duplicate Entry Representation of Search Results
23 * <draft-ietf-ldapext-ldapv3-dupent-08.txt> (EXPIRED)
24 * <http://tools.ietf.org/id/draft-ietf-ldapext-ldapv3-dupent-08.txt>
29 /* define SLAPD_OVER_DUPENT=2 to build as run-time loadable module */
30 #ifdef SLAPD_OVER_DUPENT
35 * LDAP_CONTROL_DUPENT_REQUEST "2.16.840.1.113719.1.27.101.1"
36 * LDAP_CONTROL_DUPENT_RESPONSE "2.16.840.1.113719.1.27.101.2"
37 * LDAP_CONTROL_DUPENT_ENTRY "2.16.840.1.113719.1.27.101.3"
39 * are already defined in <ldap.h>
43 * support for no attrs and "*" in AttributeDescriptionList is missing
47 #include "ac/string.h"
49 #define o_dupent o_ctrlflag[dupent_cid]
50 #define o_ctrldupent o_controls[dupent_cid]
52 static int dupent_cid;
53 static slap_overinst dupent;
55 typedef struct dupent_t {
69 BerElementBuffer berbuf;
70 BerElement *ber = (BerElement *)&berbuf;
72 BerVarray AttributeDescriptionList = NULL;
73 ber_len_t cnt = sizeof(struct berval);
75 ber_int_t PartialApplicationAllowed = 1;
79 if ( op->o_dupent != SLAP_CONTROL_NONE ) {
80 rs->sr_text = "Dupent control specified multiple times";
81 return LDAP_PROTOCOL_ERROR;
84 if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
85 rs->sr_text = "Dupent control value is absent";
86 return LDAP_PROTOCOL_ERROR;
89 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
90 rs->sr_text = "Dupent control value is empty";
91 return LDAP_PROTOCOL_ERROR;
94 ber_init2( ber, &ctrl->ldctl_value, 0 );
98 DuplicateEntryRequest ::= SEQUENCE {
99 AttributeDescriptionList, -- from [RFC2251]
100 PartialApplicationAllowed BOOLEAN DEFAULT TRUE }
102 AttributeDescriptionList ::= SEQUENCE OF
105 AttributeDescription ::= LDAPString
107 attributeDescription = AttributeType [ ";" <options> ]
111 tag = ber_skip_tag( ber, &len );
112 if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
113 if ( ber_scanf( ber, "{M}", &AttributeDescriptionList, &cnt, off )
116 rs->sr_text = "Dupent control: dupentSpec decoding error";
117 rs->sr_err = LDAP_PROTOCOL_ERROR;
120 tag = ber_skip_tag( ber, &len );
121 if ( tag == LBER_BOOLEAN ) {
122 /* NOTE: PartialApplicationAllowed is ignored, since the control
123 * can always be honored
125 if ( ber_scanf( ber, "b", &PartialApplicationAllowed ) == LBER_ERROR )
127 rs->sr_text = "Dupent control: dupentSpec decoding error";
128 rs->sr_err = LDAP_PROTOCOL_ERROR;
131 tag = ber_skip_tag( ber, &len );
133 if ( len || tag != LBER_DEFAULT ) {
134 rs->sr_text = "Dupent control: dupentSpec decoding error";
135 rs->sr_err = LDAP_PROTOCOL_ERROR;
139 ds = (dupent_t *)op->o_tmpcalloc( 1,
140 sizeof(dupent_t) + sizeof(AttributeName)*cnt,
143 ds->ds_paa = PartialApplicationAllowed;
146 ds->ds_flags |= SLAP_USERATTRS_YES;
151 ds->ds_an = (AttributeName *)&ds[ 1 ];
153 for ( i = 0, c = 0; i < cnt; i++ ) {
157 AttributeDescription *ad = NULL;
159 if ( bvmatch( &AttributeDescriptionList[i],
160 slap_bv_all_user_attrs ) )
162 if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
163 rs->sr_text = "Dupent control: AttributeDescription decoding error";
164 rs->sr_err = LDAP_PROTOCOL_ERROR;
168 ds->ds_flags |= SLAP_USERATTRS_YES;
172 rc = slap_bv2ad( &AttributeDescriptionList[i], &ad, &text );
173 if ( rc != LDAP_SUCCESS ) {
177 ds->ds_an[c].an_desc = ad;
178 ds->ds_an[c].an_name = ad->ad_cname;
180 /* FIXME: not specified; consider this an error, just in case */
181 for ( j = 0; j < c; j++ ) {
182 if ( ds->ds_an[c].an_desc == ds->ds_an[j].an_desc ) {
183 rs->sr_text = "Dupent control: AttributeDescription must be unique within AttributeDescriptionList";
184 rs->sr_err = LDAP_PROTOCOL_ERROR;
194 if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
195 /* purge user attrs */
196 for ( i = 0; i < ds->ds_nattrs; ) {
197 if ( is_at_operational( ds->ds_an[i].an_desc->ad_type ) ) {
203 if ( i < ds->ds_nattrs ) {
204 ds->ds_an[i] = ds->ds_an[ds->ds_nattrs];
210 op->o_ctrldupent = (void *)ds;
212 op->o_dupent = ctrl->ldctl_iscritical
213 ? SLAP_CONTROL_CRITICAL
214 : SLAP_CONTROL_NONCRITICAL;
216 rs->sr_err = LDAP_SUCCESS;
219 if ( rs->sr_err != LDAP_SUCCESS ) {
220 op->o_tmpfree( ds, op->o_tmpmemctx );
223 if ( AttributeDescriptionList != NULL ) {
224 ber_memfree_x( AttributeDescriptionList, op->o_tmpmemctx );
230 typedef struct dupent_cb_t {
231 slap_overinst *dc_on;
236 typedef struct valnum_t {
239 struct berval vals[2];
240 struct berval nvals[2];
245 dupent_response_done( Operation *op, SlapReply *rs )
247 BerElementBuffer berbuf;
248 BerElement *ber = (BerElement *) &berbuf;
249 struct berval ctrlval;
250 LDAPControl *ctrl, *ctrlsp[2];
252 ber_init2( ber, NULL, LBER_USE_DER );
256 DuplicateEntryResponseDone ::= SEQUENCE {
257 resultCode, -- From [RFC2251]
258 errorMessage [0] LDAPString OPTIONAL,
259 attribute [1] AttributeDescription OPTIONAL }
263 ber_printf( ber, "{i}", rs->sr_err );
264 if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
266 if ( op->o_dupent == SLAP_CONTROL_CRITICAL ) {
267 return LDAP_CONSTRAINT_VIOLATION;
269 return SLAP_CB_CONTINUE;
272 ctrl = op->o_tmpcalloc( 1,
273 sizeof( LDAPControl ) + ctrlval.bv_len + 1,
275 ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
276 ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_RESPONSE;
277 ctrl->ldctl_iscritical = 0;
278 ctrl->ldctl_value.bv_len = ctrlval.bv_len;
279 AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
280 ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
286 slap_add_ctrls( op, rs, ctrlsp );
288 return SLAP_CB_CONTINUE;
292 dupent_response_entry_1level(
300 int i, rc = LDAP_SUCCESS;
302 for ( i = 0; i < valnum[level].ap->a_numvals; i++ ) {
303 LDAPControl *ctrl = NULL, *ctrlsp[2];
305 valnum[level].a.a_vals[0] = valnum[level].ap->a_vals[i];
306 if ( valnum[level].ap->a_nvals != valnum[level].ap->a_vals ) {
307 valnum[level].a.a_nvals[0] = valnum[level].ap->a_nvals[i];
310 if ( level < nattrs - 1 ) {
311 rc = dupent_response_entry_1level( op, rs,
312 e, valnum, nattrs, level + 1 );
313 if ( rc != LDAP_SUCCESS ) {
320 /* NOTE: add the control all times, under the assumption
321 * send_search_entry() honors the REP_CTRLS_MUSTBEFREED
322 * set by slap_add_ctrls(); this is not true (ITS#6629)
324 ctrl = op->o_tmpcalloc( 1, sizeof( LDAPControl ), op->o_tmpmemctx );
325 ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_ENTRY;
326 ctrl->ldctl_iscritical = 0;
330 slap_add_ctrls( op, rs, ctrlsp );
332 /* do the real send */
334 rc = send_search_entry( op, rs );
335 if ( rc != LDAP_SUCCESS ) {
344 dupent_attr_prepare( dupent_t *ds, Entry *e, valnum_t *valnum, int nattrs, int c, Attribute **app, Attribute **ap_listp )
347 *app = (*app)->a_next;
349 valnum[c].ap->a_next = *ap_listp;
350 *ap_listp = valnum[c].ap;
352 valnum[c].a = *valnum[c].ap;
353 if ( c < nattrs - 1 ) {
354 valnum[c].a.a_next = &valnum[c + 1].a;
356 valnum[c].a.a_next = NULL;
358 valnum[c].a.a_numvals = 1;
359 valnum[c].a.a_vals = valnum[c].vals;
360 BER_BVZERO( &valnum[c].vals[1] );
361 if ( valnum[c].ap->a_nvals != valnum[c].ap->a_vals ) {
362 valnum[c].a.a_nvals = valnum[c].nvals;
363 BER_BVZERO( &valnum[c].nvals[1] );
365 valnum[c].a.a_nvals = valnum[c].a.a_vals;
370 dupent_response_entry( Operation *op, SlapReply *rs )
372 dupent_cb_t *dc = (dupent_cb_t *)op->o_callback->sc_private;
374 valnum_t *valnum = NULL;
375 Attribute **app, *ap_list = NULL;
380 assert( rs->sr_type == REP_SEARCH );
382 for ( i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
385 ap = attr_find( rs->sr_entry->e_attrs,
386 dc->dc_ds->ds_an[ i ].an_desc );
387 if ( ap && ap->a_numvals > 1 ) {
392 if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
395 for ( ap = rs->sr_entry->e_attrs; ap != NULL; ap = ap->a_next ) {
396 if ( !is_at_operational( ap->a_desc->ad_type ) && ap->a_numvals > 1 ) {
403 return SLAP_CB_CONTINUE;
406 rs_entry2modifiable( op, rs, dc->dc_on );
407 rs->sr_flags &= ~(REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED);
410 valnum = op->o_tmpcalloc( sizeof(valnum_t), nattrs, op->o_tmpmemctx );
412 for ( c = 0, i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
413 for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
414 if ( (*app)->a_desc == dc->dc_ds->ds_an[ i ].an_desc ) {
419 if ( *app != NULL && (*app)->a_numvals > 1 ) {
420 assert( c < nattrs );
421 dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
426 if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
427 for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
428 if ( !is_at_operational( (*app)->a_desc->ad_type ) && (*app)->a_numvals > 1 ) {
429 assert( c < nattrs );
430 dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
436 for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next )
441 /* NOTE: since send_search_entry() does not honor the
442 * REP_CTRLS_MUSTBEFREED flag set by slap_add_ctrls(),
443 * the control could be added here once for all (ITS#6629)
447 rc = dupent_response_entry_1level( op, rs, e, valnum, nattrs, 0 );
454 op->o_tmpfree( valnum, op->o_tmpmemctx );
460 dupent_response( Operation *op, SlapReply *rs )
462 dupent_cb_t *dc = (dupent_cb_t *)op->o_callback->sc_private;
465 return SLAP_CB_CONTINUE;
468 switch ( rs->sr_type ) {
470 return dupent_response_done( op, rs );
473 return dupent_response_entry( op, rs );
482 return SLAP_CB_CONTINUE;
486 dupent_cleanup( Operation *op, SlapReply *rs )
488 if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
489 op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
490 op->o_callback = NULL;
492 op->o_tmpfree( op->o_ctrldupent, op->o_tmpmemctx );
493 op->o_ctrldupent = NULL;
496 return SLAP_CB_CONTINUE;
500 dupent_op_search( Operation *op, SlapReply *rs )
502 if ( op->o_dupent != SLAP_CONTROL_NONE ) {
506 sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( dupent_cb_t ), op->o_tmpmemctx );
508 dc = (dupent_cb_t *)&sc[ 1 ];
509 dc->dc_on = (slap_overinst *)op->o_bd->bd_info;
510 dc->dc_ds = (dupent_t *)op->o_ctrldupent;
513 sc->sc_response = dupent_response;
514 sc->sc_cleanup = dupent_cleanup;
515 sc->sc_private = (void *)dc;
517 sc->sc_next = op->o_callback->sc_next;
518 op->o_callback->sc_next = sc;
521 return SLAP_CB_CONTINUE;
524 #if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
526 #endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
528 dupent_initialize( void )
532 rc = register_supported_control( LDAP_CONTROL_DUPENT,
533 SLAP_CTRL_SEARCH, NULL,
534 dupent_parseCtrl, &dupent_cid );
535 if ( rc != LDAP_SUCCESS ) {
536 Debug( LDAP_DEBUG_ANY,
537 "dupent_initialize: Failed to register control (%d)\n",
542 dupent.on_bi.bi_type = "dupent";
544 dupent.on_bi.bi_op_search = dupent_op_search;
546 return overlay_register( &dupent );
549 #if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
551 init_module( int argc, char *argv[] )
553 return dupent_initialize();
555 #endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
557 #endif /* SLAPD_OVER_DUPENT */